##// END OF EJS Templates
remove some other occurences of pylab
Matthias BUSSONNIER -
Show More
@@ -1,718 +1,718 b''
1 1 """A simple configuration system.
2 2
3 3 Inheritance diagram:
4 4
5 5 .. inheritance-diagram:: IPython.config.loader
6 6 :parts: 3
7 7
8 8 Authors
9 9 -------
10 10 * Brian Granger
11 11 * Fernando Perez
12 12 * Min RK
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Copyright (C) 2008-2011 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 import __builtin__ as builtin_mod
27 27 import os
28 28 import re
29 29 import sys
30 30
31 31 from IPython.external import argparse
32 32 from IPython.utils.path import filefind, get_ipython_dir
33 33 from IPython.utils import py3compat, warn
34 34 from IPython.utils.encoding import DEFAULT_ENCODING
35 35
36 36 #-----------------------------------------------------------------------------
37 37 # Exceptions
38 38 #-----------------------------------------------------------------------------
39 39
40 40
41 41 class ConfigError(Exception):
42 42 pass
43 43
44 44 class ConfigLoaderError(ConfigError):
45 45 pass
46 46
47 47 class ConfigFileNotFound(ConfigError):
48 48 pass
49 49
50 50 class ArgumentError(ConfigLoaderError):
51 51 pass
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Argparse fix
55 55 #-----------------------------------------------------------------------------
56 56
57 57 # Unfortunately argparse by default prints help messages to stderr instead of
58 58 # stdout. This makes it annoying to capture long help screens at the command
59 59 # line, since one must know how to pipe stderr, which many users don't know how
60 60 # to do. So we override the print_help method with one that defaults to
61 61 # stdout and use our class instead.
62 62
63 63 class ArgumentParser(argparse.ArgumentParser):
64 64 """Simple argparse subclass that prints help to stdout by default."""
65 65
66 66 def print_help(self, file=None):
67 67 if file is None:
68 68 file = sys.stdout
69 69 return super(ArgumentParser, self).print_help(file)
70 70
71 71 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
72 72
73 73 #-----------------------------------------------------------------------------
74 74 # Config class for holding config information
75 75 #-----------------------------------------------------------------------------
76 76
77 77
78 78 class Config(dict):
79 79 """An attribute based dict that can do smart merges."""
80 80
81 81 def __init__(self, *args, **kwds):
82 82 dict.__init__(self, *args, **kwds)
83 83 # This sets self.__dict__ = self, but it has to be done this way
84 84 # because we are also overriding __setattr__.
85 85 dict.__setattr__(self, '__dict__', self)
86 86 self._ensure_subconfig()
87 87
88 88 def _ensure_subconfig(self):
89 89 """ensure that sub-dicts that should be Config objects are
90 90
91 91 casts dicts that are under section keys to Config objects,
92 92 which is necessary for constructing Config objects from dict literals.
93 93 """
94 94 for key in self:
95 95 obj = self[key]
96 96 if self._is_section_key(key) \
97 97 and isinstance(obj, dict) \
98 98 and not isinstance(obj, Config):
99 99 dict.__setattr__(self, key, Config(obj))
100 100
101 101 def _merge(self, other):
102 102 """deprecated alias, use Config.merge()"""
103 103 self.merge(other)
104 104
105 105 def merge(self, other):
106 106 """merge another config object into this one"""
107 107 to_update = {}
108 108 for k, v in other.iteritems():
109 109 if k not in self:
110 110 to_update[k] = v
111 111 else: # I have this key
112 112 if isinstance(v, Config) and isinstance(self[k], Config):
113 113 # Recursively merge common sub Configs
114 114 self[k].merge(v)
115 115 else:
116 116 # Plain updates for non-Configs
117 117 to_update[k] = v
118 118
119 119 self.update(to_update)
120 120
121 121 def _is_section_key(self, key):
122 122 if key[0].upper()==key[0] and not key.startswith('_'):
123 123 return True
124 124 else:
125 125 return False
126 126
127 127 def __contains__(self, key):
128 128 if self._is_section_key(key):
129 129 return True
130 130 else:
131 131 return super(Config, self).__contains__(key)
132 132 # .has_key is deprecated for dictionaries.
133 133 has_key = __contains__
134 134
135 135 def _has_section(self, key):
136 136 if self._is_section_key(key):
137 137 if super(Config, self).__contains__(key):
138 138 return True
139 139 return False
140 140
141 141 def copy(self):
142 142 return type(self)(dict.copy(self))
143 143
144 144 def __copy__(self):
145 145 return self.copy()
146 146
147 147 def __deepcopy__(self, memo):
148 148 import copy
149 149 return type(self)(copy.deepcopy(self.items()))
150 150
151 151 def __getitem__(self, key):
152 152 # We cannot use directly self._is_section_key, because it triggers
153 153 # infinite recursion on top of PyPy. Instead, we manually fish the
154 154 # bound method.
155 155 is_section_key = self.__class__._is_section_key.__get__(self)
156 156
157 157 # Because we use this for an exec namespace, we need to delegate
158 158 # the lookup of names in __builtin__ to itself. This means
159 159 # that you can't have section or attribute names that are
160 160 # builtins.
161 161 try:
162 162 return getattr(builtin_mod, key)
163 163 except AttributeError:
164 164 pass
165 165 if is_section_key(key):
166 166 try:
167 167 return dict.__getitem__(self, key)
168 168 except KeyError:
169 169 c = Config()
170 170 dict.__setitem__(self, key, c)
171 171 return c
172 172 else:
173 173 return dict.__getitem__(self, key)
174 174
175 175 def __setitem__(self, key, value):
176 176 if self._is_section_key(key):
177 177 if not isinstance(value, Config):
178 178 raise ValueError('values whose keys begin with an uppercase '
179 179 'char must be Config instances: %r, %r' % (key, value))
180 180 else:
181 181 dict.__setitem__(self, key, value)
182 182
183 183 def __getattr__(self, key):
184 184 try:
185 185 return self.__getitem__(key)
186 186 except KeyError as e:
187 187 raise AttributeError(e)
188 188
189 189 def __setattr__(self, key, value):
190 190 try:
191 191 self.__setitem__(key, value)
192 192 except KeyError as e:
193 193 raise AttributeError(e)
194 194
195 195 def __delattr__(self, key):
196 196 try:
197 197 dict.__delitem__(self, key)
198 198 except KeyError as e:
199 199 raise AttributeError(e)
200 200
201 201
202 202 #-----------------------------------------------------------------------------
203 203 # Config loading classes
204 204 #-----------------------------------------------------------------------------
205 205
206 206
207 207 class ConfigLoader(object):
208 208 """A object for loading configurations from just about anywhere.
209 209
210 210 The resulting configuration is packaged as a :class:`Struct`.
211 211
212 212 Notes
213 213 -----
214 214 A :class:`ConfigLoader` does one thing: load a config from a source
215 215 (file, command line arguments) and returns the data as a :class:`Struct`.
216 216 There are lots of things that :class:`ConfigLoader` does not do. It does
217 217 not implement complex logic for finding config files. It does not handle
218 218 default values or merge multiple configs. These things need to be
219 219 handled elsewhere.
220 220 """
221 221
222 222 def __init__(self):
223 223 """A base class for config loaders.
224 224
225 225 Examples
226 226 --------
227 227
228 228 >>> cl = ConfigLoader()
229 229 >>> config = cl.load_config()
230 230 >>> config
231 231 {}
232 232 """
233 233 self.clear()
234 234
235 235 def clear(self):
236 236 self.config = Config()
237 237
238 238 def load_config(self):
239 239 """Load a config from somewhere, return a :class:`Config` instance.
240 240
241 241 Usually, this will cause self.config to be set and then returned.
242 242 However, in most cases, :meth:`ConfigLoader.clear` should be called
243 243 to erase any previous state.
244 244 """
245 245 self.clear()
246 246 return self.config
247 247
248 248
249 249 class FileConfigLoader(ConfigLoader):
250 250 """A base class for file based configurations.
251 251
252 252 As we add more file based config loaders, the common logic should go
253 253 here.
254 254 """
255 255 pass
256 256
257 257
258 258 class PyFileConfigLoader(FileConfigLoader):
259 259 """A config loader for pure python files.
260 260
261 261 This calls execfile on a plain python file and looks for attributes
262 262 that are all caps. These attribute are added to the config Struct.
263 263 """
264 264
265 265 def __init__(self, filename, path=None):
266 266 """Build a config loader for a filename and path.
267 267
268 268 Parameters
269 269 ----------
270 270 filename : str
271 271 The file name of the config file.
272 272 path : str, list, tuple
273 273 The path to search for the config file on, or a sequence of
274 274 paths to try in order.
275 275 """
276 276 super(PyFileConfigLoader, self).__init__()
277 277 self.filename = filename
278 278 self.path = path
279 279 self.full_filename = ''
280 280 self.data = None
281 281
282 282 def load_config(self):
283 283 """Load the config from a file and return it as a Struct."""
284 284 self.clear()
285 285 try:
286 286 self._find_file()
287 287 except IOError as e:
288 288 raise ConfigFileNotFound(str(e))
289 289 self._read_file_as_dict()
290 290 self._convert_to_config()
291 291 return self.config
292 292
293 293 def _find_file(self):
294 294 """Try to find the file by searching the paths."""
295 295 self.full_filename = filefind(self.filename, self.path)
296 296
297 297 def _read_file_as_dict(self):
298 298 """Load the config file into self.config, with recursive loading."""
299 299 # This closure is made available in the namespace that is used
300 300 # to exec the config file. It allows users to call
301 301 # load_subconfig('myconfig.py') to load config files recursively.
302 302 # It needs to be a closure because it has references to self.path
303 303 # and self.config. The sub-config is loaded with the same path
304 304 # as the parent, but it uses an empty config which is then merged
305 305 # with the parents.
306 306
307 307 # If a profile is specified, the config file will be loaded
308 308 # from that profile
309 309
310 310 def load_subconfig(fname, profile=None):
311 311 # import here to prevent circular imports
312 312 from IPython.core.profiledir import ProfileDir, ProfileDirError
313 313 if profile is not None:
314 314 try:
315 315 profile_dir = ProfileDir.find_profile_dir_by_name(
316 316 get_ipython_dir(),
317 317 profile,
318 318 )
319 319 except ProfileDirError:
320 320 return
321 321 path = profile_dir.location
322 322 else:
323 323 path = self.path
324 324 loader = PyFileConfigLoader(fname, path)
325 325 try:
326 326 sub_config = loader.load_config()
327 327 except ConfigFileNotFound:
328 328 # Pass silently if the sub config is not there. This happens
329 329 # when a user s using a profile, but not the default config.
330 330 pass
331 331 else:
332 332 self.config.merge(sub_config)
333 333
334 334 # Again, this needs to be a closure and should be used in config
335 335 # files to get the config being loaded.
336 336 def get_config():
337 337 return self.config
338 338
339 339 namespace = dict(
340 340 load_subconfig=load_subconfig,
341 341 get_config=get_config,
342 342 __file__=self.full_filename,
343 343 )
344 344 fs_encoding = sys.getfilesystemencoding() or 'ascii'
345 345 conf_filename = self.full_filename.encode(fs_encoding)
346 346 py3compat.execfile(conf_filename, namespace)
347 347
348 348 def _convert_to_config(self):
349 349 if self.data is None:
350 350 ConfigLoaderError('self.data does not exist')
351 351
352 352
353 353 class CommandLineConfigLoader(ConfigLoader):
354 354 """A config loader for command line arguments.
355 355
356 356 As we add more command line based loaders, the common logic should go
357 357 here.
358 358 """
359 359
360 360 def _exec_config_str(self, lhs, rhs):
361 361 """execute self.config.<lhs> = <rhs>
362 362
363 363 * expands ~ with expanduser
364 364 * tries to assign with raw eval, otherwise assigns with just the string,
365 365 allowing `--C.a=foobar` and `--C.a="foobar"` to be equivalent. *Not*
366 366 equivalent are `--C.a=4` and `--C.a='4'`.
367 367 """
368 368 rhs = os.path.expanduser(rhs)
369 369 try:
370 370 # Try to see if regular Python syntax will work. This
371 371 # won't handle strings as the quote marks are removed
372 372 # by the system shell.
373 373 value = eval(rhs)
374 374 except (NameError, SyntaxError):
375 375 # This case happens if the rhs is a string.
376 376 value = rhs
377 377
378 378 exec u'self.config.%s = value' % lhs
379 379
380 380 def _load_flag(self, cfg):
381 381 """update self.config from a flag, which can be a dict or Config"""
382 382 if isinstance(cfg, (dict, Config)):
383 383 # don't clobber whole config sections, update
384 384 # each section from config:
385 385 for sec,c in cfg.iteritems():
386 386 self.config[sec].update(c)
387 387 else:
388 388 raise TypeError("Invalid flag: %r" % cfg)
389 389
390 390 # raw --identifier=value pattern
391 391 # but *also* accept '-' as wordsep, for aliases
392 392 # accepts: --foo=a
393 393 # --Class.trait=value
394 394 # --alias-name=value
395 395 # rejects: -foo=value
396 396 # --foo
397 397 # --Class.trait
398 398 kv_pattern = re.compile(r'\-\-[A-Za-z][\w\-]*(\.[\w\-]+)*\=.*')
399 399
400 400 # just flags, no assignments, with two *or one* leading '-'
401 401 # accepts: --foo
402 402 # -foo-bar-again
403 403 # rejects: --anything=anything
404 404 # --two.word
405 405
406 406 flag_pattern = re.compile(r'\-\-?\w+[\-\w]*$')
407 407
408 408 class KeyValueConfigLoader(CommandLineConfigLoader):
409 409 """A config loader that loads key value pairs from the command line.
410 410
411 411 This allows command line options to be gives in the following form::
412 412
413 413 ipython --profile="foo" --InteractiveShell.autocall=False
414 414 """
415 415
416 416 def __init__(self, argv=None, aliases=None, flags=None):
417 417 """Create a key value pair config loader.
418 418
419 419 Parameters
420 420 ----------
421 421 argv : list
422 422 A list that has the form of sys.argv[1:] which has unicode
423 423 elements of the form u"key=value". If this is None (default),
424 424 then sys.argv[1:] will be used.
425 425 aliases : dict
426 426 A dict of aliases for configurable traits.
427 427 Keys are the short aliases, Values are the resolved trait.
428 428 Of the form: `{'alias' : 'Configurable.trait'}`
429 429 flags : dict
430 430 A dict of flags, keyed by str name. Vaues can be Config objects,
431 431 dicts, or "key=value" strings. If Config or dict, when the flag
432 432 is triggered, The flag is loaded as `self.config.update(m)`.
433 433
434 434 Returns
435 435 -------
436 436 config : Config
437 437 The resulting Config object.
438 438
439 439 Examples
440 440 --------
441 441
442 442 >>> from IPython.config.loader import KeyValueConfigLoader
443 443 >>> cl = KeyValueConfigLoader()
444 444 >>> d = cl.load_config(["--A.name='brian'","--B.number=0"])
445 445 >>> sorted(d.items())
446 446 [('A', {'name': 'brian'}), ('B', {'number': 0})]
447 447 """
448 448 self.clear()
449 449 if argv is None:
450 450 argv = sys.argv[1:]
451 451 self.argv = argv
452 452 self.aliases = aliases or {}
453 453 self.flags = flags or {}
454 454
455 455
456 456 def clear(self):
457 457 super(KeyValueConfigLoader, self).clear()
458 458 self.extra_args = []
459 459
460 460
461 461 def _decode_argv(self, argv, enc=None):
462 462 """decode argv if bytes, using stin.encoding, falling back on default enc"""
463 463 uargv = []
464 464 if enc is None:
465 465 enc = DEFAULT_ENCODING
466 466 for arg in argv:
467 467 if not isinstance(arg, unicode):
468 468 # only decode if not already decoded
469 469 arg = arg.decode(enc)
470 470 uargv.append(arg)
471 471 return uargv
472 472
473 473
474 474 def load_config(self, argv=None, aliases=None, flags=None):
475 475 """Parse the configuration and generate the Config object.
476 476
477 477 After loading, any arguments that are not key-value or
478 478 flags will be stored in self.extra_args - a list of
479 479 unparsed command-line arguments. This is used for
480 480 arguments such as input files or subcommands.
481 481
482 482 Parameters
483 483 ----------
484 484 argv : list, optional
485 485 A list that has the form of sys.argv[1:] which has unicode
486 486 elements of the form u"key=value". If this is None (default),
487 487 then self.argv will be used.
488 488 aliases : dict
489 489 A dict of aliases for configurable traits.
490 490 Keys are the short aliases, Values are the resolved trait.
491 491 Of the form: `{'alias' : 'Configurable.trait'}`
492 492 flags : dict
493 493 A dict of flags, keyed by str name. Values can be Config objects
494 494 or dicts. When the flag is triggered, The config is loaded as
495 495 `self.config.update(cfg)`.
496 496 """
497 497 self.clear()
498 498 if argv is None:
499 499 argv = self.argv
500 500 if aliases is None:
501 501 aliases = self.aliases
502 502 if flags is None:
503 503 flags = self.flags
504 504
505 505 # ensure argv is a list of unicode strings:
506 506 uargv = self._decode_argv(argv)
507 507 for idx,raw in enumerate(uargv):
508 508 # strip leading '-'
509 509 item = raw.lstrip('-')
510 510
511 511 if raw == '--':
512 512 # don't parse arguments after '--'
513 513 # this is useful for relaying arguments to scripts, e.g.
514 # ipython -i foo.py --pylab=qt -- args after '--' go-to-foo.py
514 # ipython -i foo.py --matplotlib=qt -- args after '--' go-to-foo.py
515 515 self.extra_args.extend(uargv[idx+1:])
516 516 break
517 517
518 518 if kv_pattern.match(raw):
519 519 lhs,rhs = item.split('=',1)
520 520 # Substitute longnames for aliases.
521 521 if lhs in aliases:
522 522 lhs = aliases[lhs]
523 523 if '.' not in lhs:
524 524 # probably a mistyped alias, but not technically illegal
525 525 warn.warn("Unrecognized alias: '%s', it will probably have no effect."%lhs)
526 526 try:
527 527 self._exec_config_str(lhs, rhs)
528 528 except Exception:
529 529 raise ArgumentError("Invalid argument: '%s'" % raw)
530 530
531 531 elif flag_pattern.match(raw):
532 532 if item in flags:
533 533 cfg,help = flags[item]
534 534 self._load_flag(cfg)
535 535 else:
536 536 raise ArgumentError("Unrecognized flag: '%s'"%raw)
537 537 elif raw.startswith('-'):
538 538 kv = '--'+item
539 539 if kv_pattern.match(kv):
540 540 raise ArgumentError("Invalid argument: '%s', did you mean '%s'?"%(raw, kv))
541 541 else:
542 542 raise ArgumentError("Invalid argument: '%s'"%raw)
543 543 else:
544 544 # keep all args that aren't valid in a list,
545 545 # in case our parent knows what to do with them.
546 546 self.extra_args.append(item)
547 547 return self.config
548 548
549 549 class ArgParseConfigLoader(CommandLineConfigLoader):
550 550 """A loader that uses the argparse module to load from the command line."""
551 551
552 552 def __init__(self, argv=None, aliases=None, flags=None, *parser_args, **parser_kw):
553 553 """Create a config loader for use with argparse.
554 554
555 555 Parameters
556 556 ----------
557 557
558 558 argv : optional, list
559 559 If given, used to read command-line arguments from, otherwise
560 560 sys.argv[1:] is used.
561 561
562 562 parser_args : tuple
563 563 A tuple of positional arguments that will be passed to the
564 564 constructor of :class:`argparse.ArgumentParser`.
565 565
566 566 parser_kw : dict
567 567 A tuple of keyword arguments that will be passed to the
568 568 constructor of :class:`argparse.ArgumentParser`.
569 569
570 570 Returns
571 571 -------
572 572 config : Config
573 573 The resulting Config object.
574 574 """
575 575 super(CommandLineConfigLoader, self).__init__()
576 576 self.clear()
577 577 if argv is None:
578 578 argv = sys.argv[1:]
579 579 self.argv = argv
580 580 self.aliases = aliases or {}
581 581 self.flags = flags or {}
582 582
583 583 self.parser_args = parser_args
584 584 self.version = parser_kw.pop("version", None)
585 585 kwargs = dict(argument_default=argparse.SUPPRESS)
586 586 kwargs.update(parser_kw)
587 587 self.parser_kw = kwargs
588 588
589 589 def load_config(self, argv=None, aliases=None, flags=None):
590 590 """Parse command line arguments and return as a Config object.
591 591
592 592 Parameters
593 593 ----------
594 594
595 595 args : optional, list
596 596 If given, a list with the structure of sys.argv[1:] to parse
597 597 arguments from. If not given, the instance's self.argv attribute
598 598 (given at construction time) is used."""
599 599 self.clear()
600 600 if argv is None:
601 601 argv = self.argv
602 602 if aliases is None:
603 603 aliases = self.aliases
604 604 if flags is None:
605 605 flags = self.flags
606 606 self._create_parser(aliases, flags)
607 607 self._parse_args(argv)
608 608 self._convert_to_config()
609 609 return self.config
610 610
611 611 def get_extra_args(self):
612 612 if hasattr(self, 'extra_args'):
613 613 return self.extra_args
614 614 else:
615 615 return []
616 616
617 617 def _create_parser(self, aliases=None, flags=None):
618 618 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
619 619 self._add_arguments(aliases, flags)
620 620
621 621 def _add_arguments(self, aliases=None, flags=None):
622 622 raise NotImplementedError("subclasses must implement _add_arguments")
623 623
624 624 def _parse_args(self, args):
625 625 """self.parser->self.parsed_data"""
626 626 # decode sys.argv to support unicode command-line options
627 627 enc = DEFAULT_ENCODING
628 628 uargs = [py3compat.cast_unicode(a, enc) for a in args]
629 629 self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
630 630
631 631 def _convert_to_config(self):
632 632 """self.parsed_data->self.config"""
633 633 for k, v in vars(self.parsed_data).iteritems():
634 634 exec "self.config.%s = v"%k in locals(), globals()
635 635
636 636 class KVArgParseConfigLoader(ArgParseConfigLoader):
637 637 """A config loader that loads aliases and flags with argparse,
638 638 but will use KVLoader for the rest. This allows better parsing
639 639 of common args, such as `ipython -c 'print 5'`, but still gets
640 640 arbitrary config with `ipython --InteractiveShell.use_readline=False`"""
641 641
642 642 def _add_arguments(self, aliases=None, flags=None):
643 643 self.alias_flags = {}
644 644 # print aliases, flags
645 645 if aliases is None:
646 646 aliases = self.aliases
647 647 if flags is None:
648 648 flags = self.flags
649 649 paa = self.parser.add_argument
650 650 for key,value in aliases.iteritems():
651 651 if key in flags:
652 652 # flags
653 653 nargs = '?'
654 654 else:
655 655 nargs = None
656 656 if len(key) is 1:
657 657 paa('-'+key, '--'+key, type=unicode, dest=value, nargs=nargs)
658 658 else:
659 659 paa('--'+key, type=unicode, dest=value, nargs=nargs)
660 660 for key, (value, help) in flags.iteritems():
661 661 if key in self.aliases:
662 662 #
663 663 self.alias_flags[self.aliases[key]] = value
664 664 continue
665 665 if len(key) is 1:
666 666 paa('-'+key, '--'+key, action='append_const', dest='_flags', const=value)
667 667 else:
668 668 paa('--'+key, action='append_const', dest='_flags', const=value)
669 669
670 670 def _convert_to_config(self):
671 671 """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
672 672 # remove subconfigs list from namespace before transforming the Namespace
673 673 if '_flags' in self.parsed_data:
674 674 subcs = self.parsed_data._flags
675 675 del self.parsed_data._flags
676 676 else:
677 677 subcs = []
678 678
679 679 for k, v in vars(self.parsed_data).iteritems():
680 680 if v is None:
681 681 # it was a flag that shares the name of an alias
682 682 subcs.append(self.alias_flags[k])
683 683 else:
684 684 # eval the KV assignment
685 685 self._exec_config_str(k, v)
686 686
687 687 for subc in subcs:
688 688 self._load_flag(subc)
689 689
690 690 if self.extra_args:
691 691 sub_parser = KeyValueConfigLoader()
692 692 sub_parser.load_config(self.extra_args)
693 693 self.config.merge(sub_parser.config)
694 694 self.extra_args = sub_parser.extra_args
695 695
696 696
697 697 def load_pyconfig_files(config_files, path):
698 698 """Load multiple Python config files, merging each of them in turn.
699 699
700 700 Parameters
701 701 ==========
702 702 config_files : list of str
703 703 List of config files names to load and merge into the config.
704 704 path : unicode
705 705 The full path to the location of the config files.
706 706 """
707 707 config = Config()
708 708 for cf in config_files:
709 709 loader = PyFileConfigLoader(cf, path=path)
710 710 try:
711 711 next_config = loader.load_config()
712 712 except ConfigFileNotFound:
713 713 pass
714 714 except:
715 715 raise
716 716 else:
717 717 config.merge(next_config)
718 718 return config
@@ -1,84 +1,84 b''
1 1 """ Import Qt in a manner suitable for an IPython kernel.
2 2
3 This is the import used for the `gui=qt` or `pylab=qt` initialization.
3 This is the import used for the `gui=qt` or `matplotlib=qt` initialization.
4 4
5 5 Import Priority:
6 6
7 7 if Qt4 has been imported anywhere else:
8 8 use that
9 9
10 10 if matplotlib has been imported and doesn't support v2 (<= 1.0.1):
11 11 use PyQt4 @v1
12 12
13 13 Next, ask ETS' QT_API env variable
14 14
15 15 if QT_API not set:
16 16 ask matplotlib via rcParams['backend.qt4']
17 17 if it said PyQt:
18 18 use PyQt4 @v1
19 19 elif it said PySide:
20 20 use PySide
21 21
22 22 else: (matplotlib said nothing)
23 23 # this is the default path - nobody told us anything
24 24 try:
25 25 PyQt @v1
26 26 except:
27 27 fallback on PySide
28 28 else:
29 29 use PyQt @v2 or PySide, depending on QT_API
30 30 because ETS doesn't work with PyQt @v1.
31 31
32 32 """
33 33
34 34 import os
35 35 import sys
36 36
37 37 from IPython.utils.warn import warn
38 38 from IPython.utils.version import check_version
39 39 from IPython.external.qt_loaders import (load_qt, QT_API_PYSIDE,
40 40 QT_API_PYQT, QT_API_PYQT_DEFAULT,
41 41 loaded_api)
42 42
43 43 #Constraints placed on an imported matplotlib
44 44 def matplotlib_options(mpl):
45 45 if mpl is None:
46 46 return
47 47 mpqt = mpl.rcParams.get('backend.qt4', None)
48 48 if mpqt is None:
49 49 return None
50 50 if mpqt.lower() == 'pyside':
51 51 return [QT_API_PYSIDE]
52 52 elif mpqt.lower() == 'pyqt4':
53 53 return [QT_API_PYQT_DEFAULT]
54 54 raise ImportError("unhandled value for backend.qt4 from matplotlib: %r" %
55 55 mpqt)
56 56
57 57 def get_options():
58 58 """Return a list of acceptable QT APIs, in decreasing order of
59 59 preference
60 60 """
61 61 #already imported Qt somewhere. Use that
62 62 loaded = loaded_api()
63 63 if loaded is not None:
64 64 return [loaded]
65 65
66 66 mpl = sys.modules.get('matplotlib', None)
67 67
68 68 if mpl is not None and not check_version(mpl.__version__, '1.0.2'):
69 69 #1.0.1 only supports PyQt4 v1
70 70 return [QT_API_PYQT_DEFAULT]
71 71
72 72 if os.environ.get('QT_API', None) is None:
73 73 #no ETS variable. Ask mpl, then use either
74 74 return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE]
75 75
76 76 #ETS variable present. Will fallback to external.qt
77 77 return None
78 78
79 79 api_opts = get_options()
80 80 if api_opts is not None:
81 81 QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts)
82 82
83 83 else: # use ETS variable
84 84 from IPython.external.qt import QtCore, QtGui, QtSvg, QT_API
@@ -1,392 +1,392 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The :class:`~IPython.core.application.Application` object for the command
5 5 line :command:`ipython` program.
6 6
7 7 Authors
8 8 -------
9 9
10 10 * Brian Granger
11 11 * Fernando Perez
12 12 * Min Ragan-Kelley
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Copyright (C) 2008-2011 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 from __future__ import absolute_import
27 27
28 28 import logging
29 29 import os
30 30 import sys
31 31
32 32 from IPython.config.loader import (
33 33 Config, PyFileConfigLoader, ConfigFileNotFound
34 34 )
35 35 from IPython.config.application import boolean_flag, catch_config_error
36 36 from IPython.core import release
37 37 from IPython.core import usage
38 38 from IPython.core.completer import IPCompleter
39 39 from IPython.core.crashhandler import CrashHandler
40 40 from IPython.core.formatters import PlainTextFormatter
41 41 from IPython.core.history import HistoryManager
42 42 from IPython.core.prompts import PromptManager
43 43 from IPython.core.application import (
44 44 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
45 45 )
46 46 from IPython.core.magics import ScriptMagics
47 47 from IPython.core.shellapp import (
48 48 InteractiveShellApp, shell_flags, shell_aliases
49 49 )
50 50 from IPython.terminal.interactiveshell import TerminalInteractiveShell
51 51 from IPython.utils import warn
52 52 from IPython.utils.path import get_ipython_dir, check_for_old_config
53 53 from IPython.utils.traitlets import (
54 54 Bool, List, Dict,
55 55 )
56 56
57 57 #-----------------------------------------------------------------------------
58 58 # Globals, utilities and helpers
59 59 #-----------------------------------------------------------------------------
60 60
61 61 _examples = """
62 62 ipython --pylab # start in pylab mode
63 63 ipython --pylab=qt # start in pylab mode with the qt4 backend
64 64 ipython --log-level=DEBUG # set logging to DEBUG
65 65 ipython --profile=foo # start with profile foo
66 66
67 67 ipython qtconsole # start the qtconsole GUI application
68 68 ipython help qtconsole # show the help for the qtconsole subcmd
69 69
70 70 ipython console # start the terminal-based console application
71 71 ipython help console # show the help for the console subcmd
72 72
73 73 ipython notebook # start the IPython notebook
74 74 ipython help notebook # show the help for the notebook subcmd
75 75
76 76 ipython profile create foo # create profile foo w/ default config files
77 77 ipython help profile # show the help for the profile subcmd
78 78
79 79 ipython locate # print the path to the IPython directory
80 80 ipython locate profile foo # print the path to the directory for profile `foo`
81 81
82 82 ipython nbconvert # convert notebooks to/from other formats
83 83 """
84 84
85 85 #-----------------------------------------------------------------------------
86 86 # Crash handler for this application
87 87 #-----------------------------------------------------------------------------
88 88
89 89 class IPAppCrashHandler(CrashHandler):
90 90 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
91 91
92 92 def __init__(self, app):
93 93 contact_name = release.author
94 94 contact_email = release.author_email
95 95 bug_tracker = 'https://github.com/ipython/ipython/issues'
96 96 super(IPAppCrashHandler,self).__init__(
97 97 app, contact_name, contact_email, bug_tracker
98 98 )
99 99
100 100 def make_report(self,traceback):
101 101 """Return a string containing a crash report."""
102 102
103 103 sec_sep = self.section_sep
104 104 # Start with parent report
105 105 report = [super(IPAppCrashHandler, self).make_report(traceback)]
106 106 # Add interactive-specific info we may have
107 107 rpt_add = report.append
108 108 try:
109 109 rpt_add(sec_sep+"History of session input:")
110 110 for line in self.app.shell.user_ns['_ih']:
111 111 rpt_add(line)
112 112 rpt_add('\n*** Last line of input (may not be in above history):\n')
113 113 rpt_add(self.app.shell._last_input_line+'\n')
114 114 except:
115 115 pass
116 116
117 117 return ''.join(report)
118 118
119 119 #-----------------------------------------------------------------------------
120 120 # Aliases and Flags
121 121 #-----------------------------------------------------------------------------
122 122 flags = dict(base_flags)
123 123 flags.update(shell_flags)
124 124 frontend_flags = {}
125 125 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
126 126 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
127 127 'Turn on auto editing of files with syntax errors.',
128 128 'Turn off auto editing of files with syntax errors.'
129 129 )
130 130 addflag('banner', 'TerminalIPythonApp.display_banner',
131 131 "Display a banner upon starting IPython.",
132 132 "Don't display a banner upon starting IPython."
133 133 )
134 134 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
135 135 """Set to confirm when you try to exit IPython with an EOF (Control-D
136 136 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
137 137 you can force a direct exit without any confirmation.""",
138 138 "Don't prompt the user when exiting."
139 139 )
140 140 addflag('term-title', 'TerminalInteractiveShell.term_title',
141 141 "Enable auto setting the terminal title.",
142 142 "Disable auto setting the terminal title."
143 143 )
144 144 classic_config = Config()
145 145 classic_config.InteractiveShell.cache_size = 0
146 146 classic_config.PlainTextFormatter.pprint = False
147 147 classic_config.PromptManager.in_template = '>>> '
148 148 classic_config.PromptManager.in2_template = '... '
149 149 classic_config.PromptManager.out_template = ''
150 150 classic_config.InteractiveShell.separate_in = ''
151 151 classic_config.InteractiveShell.separate_out = ''
152 152 classic_config.InteractiveShell.separate_out2 = ''
153 153 classic_config.InteractiveShell.colors = 'NoColor'
154 154 classic_config.InteractiveShell.xmode = 'Plain'
155 155
156 156 frontend_flags['classic']=(
157 157 classic_config,
158 158 "Gives IPython a similar feel to the classic Python prompt."
159 159 )
160 160 # # log doesn't make so much sense this way anymore
161 161 # paa('--log','-l',
162 162 # action='store_true', dest='InteractiveShell.logstart',
163 163 # help="Start logging to the default log file (./ipython_log.py).")
164 164 #
165 165 # # quick is harder to implement
166 166 frontend_flags['quick']=(
167 167 {'TerminalIPythonApp' : {'quick' : True}},
168 168 "Enable quick startup with no config files."
169 169 )
170 170
171 171 frontend_flags['i'] = (
172 172 {'TerminalIPythonApp' : {'force_interact' : True}},
173 173 """If running code from the command line, become interactive afterwards.
174 174 Note: can also be given simply as '-i.'"""
175 175 )
176 176 flags.update(frontend_flags)
177 177
178 178 aliases = dict(base_aliases)
179 179 aliases.update(shell_aliases)
180 180
181 181 #-----------------------------------------------------------------------------
182 182 # Main classes and functions
183 183 #-----------------------------------------------------------------------------
184 184
185 185
186 186 class LocateIPythonApp(BaseIPythonApplication):
187 187 description = """print the path to the IPython dir"""
188 188 subcommands = Dict(dict(
189 189 profile=('IPython.core.profileapp.ProfileLocate',
190 190 "print the path to an IPython profile directory",
191 191 ),
192 192 ))
193 193 def start(self):
194 194 if self.subapp is not None:
195 195 return self.subapp.start()
196 196 else:
197 197 print self.ipython_dir
198 198
199 199
200 200 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
201 201 name = u'ipython'
202 202 description = usage.cl_usage
203 203 crash_handler_class = IPAppCrashHandler
204 204 examples = _examples
205 205
206 206 flags = Dict(flags)
207 207 aliases = Dict(aliases)
208 208 classes = List()
209 209 def _classes_default(self):
210 210 """This has to be in a method, for TerminalIPythonApp to be available."""
211 211 return [
212 212 InteractiveShellApp, # ShellApp comes before TerminalApp, because
213 213 self.__class__, # it will also affect subclasses (e.g. QtConsole)
214 214 TerminalInteractiveShell,
215 215 PromptManager,
216 216 HistoryManager,
217 217 ProfileDir,
218 218 PlainTextFormatter,
219 219 IPCompleter,
220 220 ScriptMagics,
221 221 ]
222 222
223 223 subcommands = Dict(dict(
224 224 qtconsole=('IPython.qt.console.qtconsoleapp.IPythonQtConsoleApp',
225 225 """Launch the IPython Qt Console."""
226 226 ),
227 227 notebook=('IPython.html.notebookapp.NotebookApp',
228 228 """Launch the IPython HTML Notebook Server."""
229 229 ),
230 230 profile = ("IPython.core.profileapp.ProfileApp",
231 231 "Create and manage IPython profiles."
232 232 ),
233 233 kernel = ("IPython.kernel.zmq.kernelapp.IPKernelApp",
234 234 "Start a kernel without an attached frontend."
235 235 ),
236 236 console=('IPython.terminal.console.app.ZMQTerminalIPythonApp',
237 237 """Launch the IPython terminal-based Console."""
238 238 ),
239 239 locate=('IPython.terminal.ipapp.LocateIPythonApp',
240 240 LocateIPythonApp.description
241 241 ),
242 242 history=('IPython.core.historyapp.HistoryApp',
243 243 "Manage the IPython history database."
244 244 ),
245 245 nbconvert=('IPython.nbconvert.nbconvertapp.NbConvertApp',
246 246 "Convert notebooks to/from other formats."
247 247 ),
248 248 ))
249 249
250 250 # *do* autocreate requested profile, but don't create the config file.
251 251 auto_create=Bool(True)
252 252 # configurables
253 253 ignore_old_config=Bool(False, config=True,
254 254 help="Suppress warning messages about legacy config files"
255 255 )
256 256 quick = Bool(False, config=True,
257 257 help="""Start IPython quickly by skipping the loading of config files."""
258 258 )
259 259 def _quick_changed(self, name, old, new):
260 260 if new:
261 261 self.load_config_file = lambda *a, **kw: None
262 262 self.ignore_old_config=True
263 263
264 264 display_banner = Bool(True, config=True,
265 265 help="Whether to display a banner upon starting IPython."
266 266 )
267 267
268 268 # if there is code of files to run from the cmd line, don't interact
269 269 # unless the --i flag (App.force_interact) is true.
270 270 force_interact = Bool(False, config=True,
271 271 help="""If a command or file is given via the command-line,
272 272 e.g. 'ipython foo.py"""
273 273 )
274 274 def _force_interact_changed(self, name, old, new):
275 275 if new:
276 276 self.interact = True
277 277
278 278 def _file_to_run_changed(self, name, old, new):
279 279 if new:
280 280 self.something_to_run = True
281 281 if new and not self.force_interact:
282 282 self.interact = False
283 283 _code_to_run_changed = _file_to_run_changed
284 284 _module_to_run_changed = _file_to_run_changed
285 285
286 286 # internal, not-configurable
287 287 interact=Bool(True)
288 288 something_to_run=Bool(False)
289 289
290 290 def parse_command_line(self, argv=None):
291 291 """override to allow old '-pylab' flag with deprecation warning"""
292 292
293 293 argv = sys.argv[1:] if argv is None else argv
294 294
295 295 if '-pylab' in argv:
296 296 # deprecated `-pylab` given,
297 297 # warn and transform into current syntax
298 298 argv = argv[:] # copy, don't clobber
299 299 idx = argv.index('-pylab')
300 warn.warn("`-pylab` flag has been deprecated.\n"
301 " Use `--pylab` instead, or `--pylab=foo` to specify a backend.")
300 warn.warn("`-pylab` and `--pylab` flags have been deprecated.\n"
301 " Use `--matplotlib=<backend>` and import pylab manually.")
302 302 sub = '--pylab'
303 303 if len(argv) > idx+1:
304 304 # check for gui arg, as in '-pylab qt'
305 305 gui = argv[idx+1]
306 306 if gui in ('wx', 'qt', 'qt4', 'gtk', 'auto'):
307 307 sub = '--pylab='+gui
308 308 argv.pop(idx+1)
309 309 argv[idx] = sub
310 310
311 311 return super(TerminalIPythonApp, self).parse_command_line(argv)
312 312
313 313 @catch_config_error
314 314 def initialize(self, argv=None):
315 315 """Do actions after construct, but before starting the app."""
316 316 super(TerminalIPythonApp, self).initialize(argv)
317 317 if self.subapp is not None:
318 318 # don't bother initializing further, starting subapp
319 319 return
320 320 if not self.ignore_old_config:
321 321 check_for_old_config(self.ipython_dir)
322 322 # print self.extra_args
323 323 if self.extra_args and not self.something_to_run:
324 324 self.file_to_run = self.extra_args[0]
325 325 self.init_path()
326 326 # create the shell
327 327 self.init_shell()
328 328 # and draw the banner
329 329 self.init_banner()
330 330 # Now a variety of things that happen after the banner is printed.
331 331 self.init_gui_pylab()
332 332 self.init_extensions()
333 333 self.init_code()
334 334
335 335 def init_shell(self):
336 336 """initialize the InteractiveShell instance"""
337 337 # Create an InteractiveShell instance.
338 338 # shell.display_banner should always be False for the terminal
339 339 # based app, because we call shell.show_banner() by hand below
340 340 # so the banner shows *before* all extension loading stuff.
341 341 self.shell = TerminalInteractiveShell.instance(parent=self,
342 342 display_banner=False, profile_dir=self.profile_dir,
343 343 ipython_dir=self.ipython_dir)
344 344 self.shell.configurables.append(self)
345 345
346 346 def init_banner(self):
347 347 """optionally display the banner"""
348 348 if self.display_banner and self.interact:
349 349 self.shell.show_banner()
350 350 # Make sure there is a space below the banner.
351 351 if self.log_level <= logging.INFO: print
352 352
353 353 def _pylab_changed(self, name, old, new):
354 354 """Replace --pylab='inline' with --pylab='auto'"""
355 355 if new == 'inline':
356 356 warn.warn("'inline' not available as pylab backend, "
357 357 "using 'auto' instead.")
358 358 self.pylab = 'auto'
359 359
360 360 def start(self):
361 361 if self.subapp is not None:
362 362 return self.subapp.start()
363 363 # perform any prexec steps:
364 364 if self.interact:
365 365 self.log.debug("Starting IPython's mainloop...")
366 366 self.shell.mainloop()
367 367 else:
368 368 self.log.debug("IPython not interactive...")
369 369
370 370
371 371 def load_default_config(ipython_dir=None):
372 372 """Load the default config file from the default ipython_dir.
373 373
374 374 This is useful for embedded shells.
375 375 """
376 376 if ipython_dir is None:
377 377 ipython_dir = get_ipython_dir()
378 378 profile_dir = os.path.join(ipython_dir, 'profile_default')
379 379 cl = PyFileConfigLoader("ipython_config.py", profile_dir)
380 380 try:
381 381 config = cl.load_config()
382 382 except ConfigFileNotFound:
383 383 # no config found
384 384 config = Config()
385 385 return config
386 386
387 387
388 388 launch_new_instance = TerminalIPythonApp.launch_instance
389 389
390 390
391 391 if __name__ == '__main__':
392 392 launch_new_instance()
@@ -1,106 +1,106 b''
1 1 #!/usr/bin/env python
2 2 """
3 3 A Simple wx example to test IPython's event loop integration.
4 4
5 5 To run this do:
6 6
7 In [5]: %gui wx # or start IPython with '--gui wx' or '--pylab wx'
7 In [5]: %gui wx # or start IPython with '--gui wx'
8 8
9 9 In [6]: %run gui-wx.py
10 10
11 11 Ref: Modified from wxPython source code wxPython/samples/simple/simple.py
12 12 """
13 13
14 14 import wx
15 15
16 16
17 17 class MyFrame(wx.Frame):
18 18 """
19 19 This is MyFrame. It just shows a few controls on a wxPanel,
20 20 and has a simple menu.
21 21 """
22 22 def __init__(self, parent, title):
23 23 wx.Frame.__init__(self, parent, -1, title,
24 24 pos=(150, 150), size=(350, 200))
25 25
26 26 # Create the menubar
27 27 menuBar = wx.MenuBar()
28 28
29 29 # and a menu
30 30 menu = wx.Menu()
31 31
32 32 # add an item to the menu, using \tKeyName automatically
33 33 # creates an accelerator, the third param is some help text
34 34 # that will show up in the statusbar
35 35 menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample")
36 36
37 37 # bind the menu event to an event handler
38 38 self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=wx.ID_EXIT)
39 39
40 40 # and put the menu on the menubar
41 41 menuBar.Append(menu, "&File")
42 42 self.SetMenuBar(menuBar)
43 43
44 44 self.CreateStatusBar()
45 45
46 46 # Now create the Panel to put the other controls on.
47 47 panel = wx.Panel(self)
48 48
49 49 # and a few controls
50 50 text = wx.StaticText(panel, -1, "Hello World!")
51 51 text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD))
52 52 text.SetSize(text.GetBestSize())
53 53 btn = wx.Button(panel, -1, "Close")
54 54 funbtn = wx.Button(panel, -1, "Just for fun...")
55 55
56 56 # bind the button events to handlers
57 57 self.Bind(wx.EVT_BUTTON, self.OnTimeToClose, btn)
58 58 self.Bind(wx.EVT_BUTTON, self.OnFunButton, funbtn)
59 59
60 60 # Use a sizer to layout the controls, stacked vertically and with
61 61 # a 10 pixel border around each
62 62 sizer = wx.BoxSizer(wx.VERTICAL)
63 63 sizer.Add(text, 0, wx.ALL, 10)
64 64 sizer.Add(btn, 0, wx.ALL, 10)
65 65 sizer.Add(funbtn, 0, wx.ALL, 10)
66 66 panel.SetSizer(sizer)
67 67 panel.Layout()
68 68
69 69
70 70 def OnTimeToClose(self, evt):
71 71 """Event handler for the button click."""
72 72 print("See ya later!")
73 73 self.Close()
74 74
75 75 def OnFunButton(self, evt):
76 76 """Event handler for the button click."""
77 77 print("Having fun yet?")
78 78
79 79
80 80 class MyApp(wx.App):
81 81 def OnInit(self):
82 82 frame = MyFrame(None, "Simple wxPython App")
83 83 self.SetTopWindow(frame)
84 84
85 85 print("Print statements go to this stdout window by default.")
86 86
87 87 frame.Show(True)
88 88 return True
89 89
90 90
91 91 if __name__ == '__main__':
92 92
93 93 app = wx.GetApp()
94 94 if app is None:
95 95 app = MyApp(redirect=False, clearSigInt=False)
96 96 else:
97 97 frame = MyFrame(None, "Simple wxPython App")
98 98 app.SetTopWindow(frame)
99 99 print("Print statements go to this stdout window by default.")
100 100 frame.Show(True)
101 101
102 102 try:
103 103 from IPython.lib.inputhook import enable_wx
104 104 enable_wx(app)
105 105 except ImportError:
106 106 app.MainLoop()
General Comments 0
You need to be logged in to leave comments. Login now