Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'master' of git://github.com/ipython/ipython into fronte…

…nd-logging
  • Loading branch information...
commit 5809ebf294b4615e555d09e29b4964212da29232 2 parents 85382a1 + 5cd1ae1
Omar Andres Zapata Mesa omazapa authored

Showing 59 changed files with 2,502 additions and 870 deletions. Show diff stats Hide diff stats

  1. +230 0 IPython/config/application.py
  2. +88 2 IPython/config/configurable.py
  3. +120 8 IPython/config/loader.py
  4. +105 0 IPython/config/tests/test_application.py
  5. +48 8 IPython/config/tests/test_configurable.py
  6. +22 6 IPython/config/tests/test_loader.py
  7. +8 0 IPython/core/display.py
  8. +53 17 IPython/core/formatters.py
  9. +2 2 IPython/core/history.py
  10. +82 40 IPython/core/interactiveshell.py
  11. +1 2  IPython/core/logger.py
  12. +170 139 IPython/core/magic.py
  13. +80 231 IPython/core/oinspect.py
  14. +19 0 IPython/core/tests/test_logger.py
  15. +42 0 IPython/core/tests/test_oinspect.py
  16. +1 1  IPython/core/usage.py
  17. +4 4 IPython/extensions/parallelmagic.py
  18. +2 2 IPython/extensions/{sympy_printing.py → sympyprinting.py}
  19. +5 5 IPython/frontend/qt/console/ipython_widget.py
  20. +2 2 IPython/frontend/terminal/interactiveshell.py
  21. +49 1 IPython/lib/latextools.py
  22. +28 15 IPython/lib/pretty.py
  23. +2 1  IPython/lib/pylabtools.py
  24. +3 3 IPython/parallel/__init__.py
  25. +30 1 IPython/parallel/apps/clusterdir.py
  26. +43 19 IPython/parallel/apps/ipclusterapp.py
  27. +65 1 IPython/parallel/client/client.py
  28. +4 4 IPython/parallel/client/remotefunction.py
  29. +23 15 IPython/parallel/client/view.py
  30. +1 1  IPython/parallel/controller/dictdb.py
  31. +132 43 IPython/parallel/controller/hub.py
  32. +17 12 IPython/parallel/controller/mongodb.py
  33. +76 32 IPython/parallel/controller/scheduler.py
  34. +25 11 IPython/parallel/controller/sqlitedb.py
  35. +32 23 IPython/parallel/streamsession.py
  36. +1 1  IPython/parallel/tests/__init__.py
  37. +30 0 IPython/parallel/tests/test_client.py
  38. +7 19 IPython/parallel/tests/test_db.py
  39. +120 0 IPython/parallel/tests/test_lbview.py
  40. +37 0 IPython/parallel/tests/test_mongodb.py
  41. +1 13 IPython/parallel/tests/test_view.py
  42. +0 7 IPython/testing/decorators.py
  43. +4 1 IPython/testing/iptest.py
  44. +1 3 IPython/testing/plugin/test_refs.py
  45. +15 0 IPython/testing/skipdoctest.py
  46. +4 3 IPython/testing/tests/test_decorators.py
  47. +2 0  IPython/utils/path.py
  48. +11 1 IPython/utils/tests/test_path.py
  49. +75 2 IPython/utils/tests/test_traitlets.py
  50. +280 22 IPython/utils/traitlets.py
  51. +8 3 IPython/zmq/ipkernel.py
  52. +1 1  IPython/zmq/session.py
  53. +22 140 IPython/zmq/zmqshell.py
  54. +7 3 docs/do_sphinx.py
  55. +96 0 docs/examples/core/appconfig.py
  56. +27 0 docs/examples/core/display.py
  57. +1 0  docs/source/parallel/index.txt
  58. +114 0 docs/source/parallel/parallel_db.txt
  59. +24 0 docs/source/parallel/parallel_task.txt
230 IPython/config/application.py
... ... @@ -0,0 +1,230 @@
  1 +# encoding: utf-8
  2 +"""
  3 +A base class for a configurable application.
  4 +
  5 +Authors:
  6 +
  7 +* Brian Granger
  8 +"""
  9 +
  10 +#-----------------------------------------------------------------------------
  11 +# Copyright (C) 2008-2011 The IPython Development Team
  12 +#
  13 +# Distributed under the terms of the BSD License. The full license is in
  14 +# the file COPYING, distributed as part of this software.
  15 +#-----------------------------------------------------------------------------
  16 +
  17 +#-----------------------------------------------------------------------------
  18 +# Imports
  19 +#-----------------------------------------------------------------------------
  20 +
  21 +from copy import deepcopy
  22 +import logging
  23 +import sys
  24 +
  25 +from IPython.config.configurable import SingletonConfigurable
  26 +from IPython.config.loader import (
  27 + KeyValueConfigLoader, PyFileConfigLoader, Config
  28 +)
  29 +
  30 +from IPython.utils.traitlets import (
  31 + Unicode, List, Int, Enum, Dict
  32 +)
  33 +from IPython.utils.text import indent
  34 +
  35 +#-----------------------------------------------------------------------------
  36 +# Descriptions for
  37 +#-----------------------------------------------------------------------------
  38 +
  39 +flag_description = """
  40 +Flags are command-line arguments passed as '--<flag>'.
  41 +These take no parameters, unlike regular key-value arguments.
  42 +They are typically used for setting boolean flags, or enabling
  43 +modes that involve setting multiple options together.
  44 +""".strip() # trim newlines of front and back
  45 +
  46 +alias_description = """
  47 +These are commonly set parameters, given abbreviated aliases for convenience.
  48 +They are set in the same `name=value` way as class parameters, where
  49 +<name> is replaced by the real parameter for which it is an alias.
  50 +""".strip() # trim newlines of front and back
  51 +
  52 +keyvalue_description = """
  53 +Parameters are set from command-line arguments of the form:
  54 +`Class.trait=value`. Parameters will *never* be prefixed with '-'.
  55 +This line is evaluated in Python, so simple expressions are allowed, e.g.
  56 + `C.a='range(3)'` For setting C.a=[0,1,2]
  57 +""".strip() # trim newlines of front and back
  58 +
  59 +#-----------------------------------------------------------------------------
  60 +# Application class
  61 +#-----------------------------------------------------------------------------
  62 +
  63 +
  64 +class Application(SingletonConfigurable):
  65 + """A singleton application with full configuration support."""
  66 +
  67 + # The name of the application, will usually match the name of the command
  68 + # line application
  69 + app_name = Unicode(u'application')
  70 +
  71 + # The description of the application that is printed at the beginning
  72 + # of the help.
  73 + description = Unicode(u'This is an application.')
  74 + # default section descriptions
  75 + flag_description = Unicode(flag_description)
  76 + alias_description = Unicode(alias_description)
  77 + keyvalue_description = Unicode(keyvalue_description)
  78 +
  79 +
  80 + # A sequence of Configurable subclasses whose config=True attributes will
  81 + # be exposed at the command line.
  82 + classes = List([])
  83 +
  84 + # The version string of this application.
  85 + version = Unicode(u'0.0')
  86 +
  87 + # The log level for the application
  88 + log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
  89 + config=True,
  90 + help="Set the log level (0,10,20,30,40,50).")
  91 +
  92 + # the alias map for configurables
  93 + aliases = Dict(dict(log_level='Application.log_level'))
  94 +
  95 + # flags for loading Configurables or store_const style flags
  96 + # flags are loaded from this dict by '--key' flags
  97 + # this must be a dict of two-tuples, the first element being the Config/dict
  98 + # and the second being the help string for the flag
  99 + flags = Dict()
  100 +
  101 +
  102 + def __init__(self, **kwargs):
  103 + SingletonConfigurable.__init__(self, **kwargs)
  104 + # Add my class to self.classes so my attributes appear in command line
  105 + # options.
  106 + self.classes.insert(0, self.__class__)
  107 +
  108 + # ensure self.flags dict is valid
  109 + for key,value in self.flags.iteritems():
  110 + assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
  111 + assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
  112 + assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
  113 + self.init_logging()
  114 +
  115 + def init_logging(self):
  116 + """Start logging for this application.
  117 +
  118 + The default is to log to stdout using a StreaHandler. The log level
  119 + starts at loggin.WARN, but this can be adjusted by setting the
  120 + ``log_level`` attribute.
  121 + """
  122 + self.log = logging.getLogger(self.__class__.__name__)
  123 + self.log.setLevel(self.log_level)
  124 + self._log_handler = logging.StreamHandler()
  125 + self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
  126 + self._log_handler.setFormatter(self._log_formatter)
  127 + self.log.addHandler(self._log_handler)
  128 +
  129 + def _log_level_changed(self, name, old, new):
  130 + """Adjust the log level when log_level is set."""
  131 + self.log.setLevel(new)
  132 +
  133 + def print_alias_help(self):
  134 + """print the alias part of the help"""
  135 + if not self.aliases:
  136 + return
  137 +
  138 + print "Aliases"
  139 + print "-------"
  140 + print self.alias_description
  141 + print
  142 +
  143 + classdict = {}
  144 + for c in self.classes:
  145 + classdict[c.__name__] = c
  146 +
  147 + for alias, longname in self.aliases.iteritems():
  148 + classname, traitname = longname.split('.',1)
  149 + cls = classdict[classname]
  150 +
  151 + trait = cls.class_traits(config=True)[traitname]
  152 + help = trait.get_metadata('help')
  153 + print alias, "(%s)"%longname, ':', trait.__class__.__name__
  154 + if help:
  155 + print indent(help)
  156 + print
  157 +
  158 + def print_flag_help(self):
  159 + """print the flag part of the help"""
  160 + if not self.flags:
  161 + return
  162 +
  163 + print "Flags"
  164 + print "-----"
  165 + print self.flag_description
  166 + print
  167 +
  168 + for m, (cfg,help) in self.flags.iteritems():
  169 + print '--'+m
  170 + print indent(help)
  171 + print
  172 +
  173 + def print_help(self):
  174 + """Print the help for each Configurable class in self.classes."""
  175 + self.print_flag_help()
  176 + self.print_alias_help()
  177 + if self.classes:
  178 + print "Class parameters"
  179 + print "----------------"
  180 + print self.keyvalue_description
  181 + print
  182 +
  183 + for cls in self.classes:
  184 + cls.class_print_help()
  185 + print
  186 +
  187 + def print_description(self):
  188 + """Print the application description."""
  189 + print self.description
  190 + print
  191 +
  192 + def print_version(self):
  193 + """Print the version string."""
  194 + print self.version
  195 +
  196 + def update_config(self, config):
  197 + """Fire the traits events when the config is updated."""
  198 + # Save a copy of the current config.
  199 + newconfig = deepcopy(self.config)
  200 + # Merge the new config into the current one.
  201 + newconfig._merge(config)
  202 + # Save the combined config as self.config, which triggers the traits
  203 + # events.
  204 + self.config = config
  205 +
  206 + def parse_command_line(self, argv=None):
  207 + """Parse the command line arguments."""
  208 + argv = sys.argv[1:] if argv is None else argv
  209 +
  210 + if '-h' in argv or '--help' in argv:
  211 + self.print_description()
  212 + self.print_help()
  213 + sys.exit(1)
  214 +
  215 + if '--version' in argv:
  216 + self.print_version()
  217 + sys.exit(1)
  218 +
  219 + loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
  220 + flags=self.flags)
  221 + config = loader.load_config()
  222 + self.update_config(config)
  223 +
  224 + def load_config_file(self, filename, path=None):
  225 + """Load a .py based config file by filename and path."""
  226 + # TODO: this raises IOError if filename does not exist.
  227 + loader = PyFileConfigLoader(filename, path=path)
  228 + config = loader.load_config()
  229 + self.update_config(config)
  230 +
90 IPython/config/configurable.py
@@ -22,11 +22,10 @@
22 22
23 23 from copy import deepcopy
24 24 import datetime
25   -from weakref import WeakValueDictionary
26 25
27   -from IPython.utils.importstring import import_item
28 26 from loader import Config
29 27 from IPython.utils.traitlets import HasTraits, Instance
  28 +from IPython.utils.text import indent
30 29
31 30
32 31 #-----------------------------------------------------------------------------
@@ -38,6 +37,9 @@ class ConfigurableError(Exception):
38 37 pass
39 38
40 39
  40 +class MultipleInstanceError(ConfigurableError):
  41 + pass
  42 +
41 43 #-----------------------------------------------------------------------------
42 44 # Configurable implementation
43 45 #-----------------------------------------------------------------------------
@@ -137,3 +139,87 @@ def _config_changed(self, name, old, new):
137 139 # shared by all instances, effectively making it a class attribute.
138 140 setattr(self, k, deepcopy(config_value))
139 141
  142 + @classmethod
  143 + def class_get_help(cls):
  144 + """Get the help string for this class in ReST format."""
  145 + cls_traits = cls.class_traits(config=True)
  146 + final_help = []
  147 + final_help.append(u'%s options' % cls.__name__)
  148 + final_help.append(len(final_help[0])*u'-')
  149 + for k, v in cls_traits.items():
  150 + help = v.get_metadata('help')
  151 + header = "%s.%s : %s" % (cls.__name__, k, v.__class__.__name__)
  152 + final_help.append(header)
  153 + if help is not None:
  154 + final_help.append(indent(help))
  155 + return '\n'.join(final_help)
  156 +
  157 + @classmethod
  158 + def class_print_help(cls):
  159 + print cls.class_get_help()
  160 +
  161 +
  162 +class SingletonConfigurable(Configurable):
  163 + """A configurable that only allows one instance.
  164 +
  165 + This class is for classes that should only have one instance of itself
  166 + or *any* subclass. To create and retrieve such a class use the
  167 + :meth:`SingletonConfigurable.instance` method.
  168 + """
  169 +
  170 + _instance = None
  171 +
  172 + @classmethod
  173 + def instance(cls, *args, **kwargs):
  174 + """Returns a global instance of this class.
  175 +
  176 + This method create a new instance if none have previously been created
  177 + and returns a previously created instance is one already exists.
  178 +
  179 + The arguments and keyword arguments passed to this method are passed
  180 + on to the :meth:`__init__` method of the class upon instantiation.
  181 +
  182 + Examples
  183 + --------
  184 +
  185 + Create a singleton class using instance, and retrieve it::
  186 +
  187 + >>> from IPython.config.configurable import SingletonConfigurable
  188 + >>> class Foo(SingletonConfigurable): pass
  189 + >>> foo = Foo.instance()
  190 + >>> foo == Foo.instance()
  191 + True
  192 +
  193 + Create a subclass that is retrived using the base class instance::
  194 +
  195 + >>> class Bar(SingletonConfigurable): pass
  196 + >>> class Bam(Bar): pass
  197 + >>> bam = Bam.instance()
  198 + >>> bam == Bar.instance()
  199 + True
  200 + """
  201 + # Create and save the instance
  202 + if cls._instance is None:
  203 + inst = cls(*args, **kwargs)
  204 + # Now make sure that the instance will also be returned by
  205 + # the subclasses instance attribute.
  206 + for subclass in cls.mro():
  207 + if issubclass(cls, subclass) and \
  208 + issubclass(subclass, SingletonConfigurable) and \
  209 + subclass != SingletonConfigurable:
  210 + subclass._instance = inst
  211 + else:
  212 + break
  213 + if isinstance(cls._instance, cls):
  214 + return cls._instance
  215 + else:
  216 + raise MultipleInstanceError(
  217 + 'Multiple incompatible subclass instances of '
  218 + '%s are being created.' % cls.__name__
  219 + )
  220 +
  221 + @classmethod
  222 + def initialized(cls):
  223 + """Has an instance been created?"""
  224 + return hasattr(cls, "_instance") and cls._instance is not None
  225 +
128 IPython/config/loader.py
... ... @@ -1,5 +1,3 @@
1   -# -*- coding: utf-8 -*-
2   -# coding: utf-8
3 1 """A simple configuration system.
4 2
5 3 Authors
@@ -20,7 +18,7 @@
20 18 #-----------------------------------------------------------------------------
21 19
22 20 import __builtin__
23   -import os
  21 +import re
24 22 import sys
25 23
26 24 from IPython.external import argparse
@@ -306,8 +304,117 @@ class CommandLineConfigLoader(ConfigLoader):
306 304 here.
307 305 """
308 306
  307 +kv_pattern = re.compile(r'[A-Za-z]\w*(\.\w+)*\=.+')
  308 +flag_pattern = re.compile(r'\-\-\w+(\-\w)*')
  309 +
  310 +class KeyValueConfigLoader(CommandLineConfigLoader):
  311 + """A config loader that loads key value pairs from the command line.
  312 +
  313 + This allows command line options to be gives in the following form::
  314 +
  315 + ipython Global.profile="foo" InteractiveShell.autocall=False
  316 + """
  317 +
  318 + def __init__(self, argv=None, aliases=None, flags=None):
  319 + """Create a key value pair config loader.
  320 +
  321 + Parameters
  322 + ----------
  323 + argv : list
  324 + A list that has the form of sys.argv[1:] which has unicode
  325 + elements of the form u"key=value". If this is None (default),
  326 + then sys.argv[1:] will be used.
  327 + aliases : dict
  328 + A dict of aliases for configurable traits.
  329 + Keys are the short aliases, Values are the resolved trait.
  330 + Of the form: `{'alias' : 'Configurable.trait'}`
  331 + flags : dict
  332 + A dict of flags, keyed by str name. Vaues can be Config objects,
  333 + dicts, or "key=value" strings. If Config or dict, when the flag
  334 + is triggered, The flag is loaded as `self.config.update(m)`.
  335 +
  336 + Returns
  337 + -------
  338 + config : Config
  339 + The resulting Config object.
  340 +
  341 + Examples
  342 + --------
  343 +
  344 + >>> from IPython.config.loader import KeyValueConfigLoader
  345 + >>> cl = KeyValueConfigLoader()
  346 + >>> cl.load_config(["foo='bar'","A.name='brian'","B.number=0"])
  347 + {'A': {'name': 'brian'}, 'B': {'number': 0}, 'foo': 'bar'}
  348 + """
  349 + if argv is None:
  350 + argv = sys.argv[1:]
  351 + self.argv = argv
  352 + self.aliases = aliases or {}
  353 + self.flags = flags or {}
  354 +
  355 + def load_config(self, argv=None, aliases=None, flags=None):
  356 + """Parse the configuration and generate the Config object.
  357 +
  358 + Parameters
  359 + ----------
  360 + argv : list, optional
  361 + A list that has the form of sys.argv[1:] which has unicode
  362 + elements of the form u"key=value". If this is None (default),
  363 + then self.argv will be used.
  364 + aliases : dict
  365 + A dict of aliases for configurable traits.
  366 + Keys are the short aliases, Values are the resolved trait.
  367 + Of the form: `{'alias' : 'Configurable.trait'}`
  368 + flags : dict
  369 + A dict of flags, keyed by str name. Values can be Config objects
  370 + or dicts. When the flag is triggered, The config is loaded as
  371 + `self.config.update(cfg)`.
  372 + """
  373 + from IPython.config.configurable import Configurable
  374 +
  375 + self.clear()
  376 + if argv is None:
  377 + argv = self.argv
  378 + if aliases is None:
  379 + aliases = self.aliases
  380 + if flags is None:
  381 + flags = self.flags
  382 +
  383 + for item in argv:
  384 + if kv_pattern.match(item):
  385 + lhs,rhs = item.split('=',1)
  386 + # Substitute longnames for aliases.
  387 + if lhs in aliases:
  388 + lhs = aliases[lhs]
  389 + exec_str = 'self.config.' + lhs + '=' + rhs
  390 + try:
  391 + # Try to see if regular Python syntax will work. This
  392 + # won't handle strings as the quote marks are removed
  393 + # by the system shell.
  394 + exec exec_str in locals(), globals()
  395 + except (NameError, SyntaxError):
  396 + # This case happens if the rhs is a string but without
  397 + # the quote marks. We add the quote marks and see if
  398 + # it succeeds. If it still fails, we let it raise.
  399 + exec_str = 'self.config.' + lhs + '="' + rhs + '"'
  400 + exec exec_str in locals(), globals()
  401 + elif flag_pattern.match(item):
  402 + # trim leading '--'
  403 + m = item[2:]
  404 + cfg,_ = flags.get(m, (None,None))
  405 + if cfg is None:
  406 + raise ValueError("Unrecognized flag: %r"%item)
  407 + elif isinstance(cfg, (dict, Config)):
  408 + # update self.config with Config:
  409 + self.config.update(cfg)
  410 + else:
  411 + raise ValueError("Invalid flag: %r"%flag)
  412 + else:
  413 + raise ValueError("Invalid argument: %r"%item)
  414 + return self.config
309 415
310 416 class ArgParseConfigLoader(CommandLineConfigLoader):
  417 + """A loader that uses the argparse module to load from the command line."""
311 418
312 419 def __init__(self, argv=None, *parser_args, **parser_kw):
313 420 """Create a config loader for use with argparse.
@@ -326,6 +433,11 @@ def __init__(self, argv=None, *parser_args, **parser_kw):
326 433 parser_kw : dict
327 434 A tuple of keyword arguments that will be passed to the
328 435 constructor of :class:`argparse.ArgumentParser`.
  436 +
  437 + Returns
  438 + -------
  439 + config : Config
  440 + The resulting Config object.
329 441 """
330 442 super(CommandLineConfigLoader, self).__init__()
331 443 if argv == None:
@@ -337,8 +449,8 @@ def __init__(self, argv=None, *parser_args, **parser_kw):
337 449 kwargs.update(parser_kw)
338 450 self.parser_kw = kwargs
339 451
340   - def load_config(self, args=None):
341   - """Parse command line arguments and return as a Struct.
  452 + def load_config(self, argv=None):
  453 + """Parse command line arguments and return as a Config object.
342 454
343 455 Parameters
344 456 ----------
@@ -348,10 +460,10 @@ def load_config(self, args=None):
348 460 arguments from. If not given, the instance's self.argv attribute
349 461 (given at construction time) is used."""
350 462 self.clear()
351   - if args is None:
352   - args = self.argv
  463 + if argv is None:
  464 + argv = self.argv
353 465 self._create_parser()
354   - self._parse_args(args)
  466 + self._parse_args(argv)
355 467 self._convert_to_config()
356 468 return self.config
357 469
105 IPython/config/tests/test_application.py
... ... @@ -0,0 +1,105 @@
  1 +"""
  2 +Tests for IPython.config.application.Application
  3 +
  4 +Authors:
  5 +
  6 +* Brian Granger
  7 +"""
  8 +
  9 +#-----------------------------------------------------------------------------
  10 +# Copyright (C) 2008-2011 The IPython Development Team
  11 +#
  12 +# Distributed under the terms of the BSD License. The full license is in
  13 +# the file COPYING, distributed as part of this software.
  14 +#-----------------------------------------------------------------------------
  15 +
  16 +#-----------------------------------------------------------------------------
  17 +# Imports
  18 +#-----------------------------------------------------------------------------
  19 +
  20 +from unittest import TestCase
  21 +
  22 +from IPython.config.configurable import Configurable
  23 +
  24 +from IPython.config.application import (
  25 + Application
  26 +)
  27 +
  28 +from IPython.utils.traitlets import (
  29 + Bool, Unicode, Int, Float, List, Dict
  30 +)
  31 +
  32 +#-----------------------------------------------------------------------------
  33 +# Code
  34 +#-----------------------------------------------------------------------------
  35 +
  36 +class Foo(Configurable):
  37 +
  38 + i = Int(0, config=True, help="The integer i.")
  39 + j = Int(1, config=True, help="The integer j.")
  40 + name = Unicode(u'Brian', config=True, help="First name.")
  41 +
  42 +
  43 +class Bar(Configurable):
  44 +
  45 + enabled = Bool(True, config=True, help="Enable bar.")
  46 +
  47 +
  48 +class MyApp(Application):
  49 +
  50 + app_name = Unicode(u'myapp')
  51 + running = Bool(False, config=True,
  52 + help="Is the app running?")
  53 + classes = List([Bar, Foo])
  54 + config_file = Unicode(u'', config=True,
  55 + help="Load this config file")
  56 +
  57 + aliases = Dict(dict(i='Foo.i',j='Foo.j',name='Foo.name',
  58 + enabled='Bar.enabled', log_level='MyApp.log_level'))
  59 +
  60 + flags = Dict(dict(enable=({'Bar': {'enabled' : True}}, "Set Bar.enabled to True"),
  61 + disable=({'Bar': {'enabled' : False}}, "Set Bar.enabled to False")))
  62 +
  63 + def init_foo(self):
  64 + self.foo = Foo(config=self.config)
  65 +
  66 + def init_bar(self):
  67 + self.bar = Bar(config=self.config)
  68 +
  69 +
  70 +class TestApplication(TestCase):
  71 +
  72 + def test_basic(self):
  73 + app = MyApp()
  74 + self.assertEquals(app.app_name, u'myapp')
  75 + self.assertEquals(app.running, False)
  76 + self.assertEquals(app.classes, [MyApp,Bar,Foo])
  77 + self.assertEquals(app.config_file, u'')
  78 +
  79 + def test_config(self):
  80 + app = MyApp()
  81 + app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=0"])
  82 + config = app.config
  83 + self.assertEquals(config.Foo.i, 10)
  84 + self.assertEquals(config.Foo.j, 10)
  85 + self.assertEquals(config.Bar.enabled, False)
  86 + self.assertEquals(config.MyApp.log_level,0)
  87 +
  88 + def test_config_propagation(self):
  89 + app = MyApp()
  90 + app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=0"])
  91 + app.init_foo()
  92 + app.init_bar()
  93 + self.assertEquals(app.foo.i, 10)
  94 + self.assertEquals(app.foo.j, 10)
  95 + self.assertEquals(app.bar.enabled, False)
  96 +
  97 + def test_alias(self):
  98 + app = MyApp()
  99 + app.parse_command_line(["--disable"])
  100 + app.init_bar()
  101 + self.assertEquals(app.bar.enabled, False)
  102 + app.parse_command_line(["--enable"])
  103 + app.init_bar()
  104 + self.assertEquals(app.bar.enabled, True)
  105 +
56 IPython/config/tests/test_configurable.py
@@ -22,10 +22,15 @@
22 22
23 23 from unittest import TestCase
24 24
25   -from IPython.config.configurable import Configurable, ConfigurableError
  25 +from IPython.config.configurable import (
  26 + Configurable,
  27 + SingletonConfigurable
  28 +)
  29 +
26 30 from IPython.utils.traitlets import (
27   - TraitError, Int, Float, Str
  31 + Int, Float, Str
28 32 )
  33 +
29 34 from IPython.config.loader import Config
30 35
31 36
@@ -35,22 +40,29 @@
35 40
36 41
37 42 class MyConfigurable(Configurable):
38   - a = Int(1, config=True)
39   - b = Float(1.0, config=True)
  43 + a = Int(1, config=True, help="The integer a.")
  44 + b = Float(1.0, config=True, help="The integer b.")
40 45 c = Str('no config')
41 46
42 47
  48 +mc_help=u"""MyConfigurable options
  49 +----------------------
  50 +MyConfigurable.a : Int
  51 + The integer a.
  52 +MyConfigurable.b : Float
  53 + The integer b."""
  54 +
43 55 class Foo(Configurable):
44   - a = Int(0, config=True)
  56 + a = Int(0, config=True, help="The integer a.")
45 57 b = Str('nope', config=True)
46 58
47 59
48 60 class Bar(Foo):
49   - b = Str('gotit', config=False)
50   - c = Float(config=True)
  61 + b = Str('gotit', config=False, help="The string b.")
  62 + c = Float(config=True, help="The string c.")
51 63
52 64
53   -class TestConfigurableConfig(TestCase):
  65 +class TestConfigurable(TestCase):
54 66
55 67 def test_default(self):
56 68 c1 = Configurable()
@@ -122,3 +134,31 @@ def test_override2(self):
122 134 self.assertEquals(c.a, 2)
123 135 self.assertEquals(c.b, 'and')
124 136 self.assertEquals(c.c, 20.0)
  137 +
  138 + def test_help(self):
  139 + self.assertEquals(MyConfigurable.class_get_help(), mc_help)
  140 +
  141 +
  142 +class TestSingletonConfigurable(TestCase):
  143 +
  144 + def test_instance(self):
  145 + from IPython.config.configurable import SingletonConfigurable
  146 + class Foo(SingletonConfigurable): pass
  147 + self.assertEquals(Foo.initialized(), False)
  148 + foo = Foo.instance()
  149 + self.assertEquals(Foo.initialized(), True)
  150 + self.assertEquals(foo, Foo.instance())
  151 + self.assertEquals(SingletonConfigurable._instance, None)
  152 +
  153 + def test_inheritance(self):
  154 + class Bar(SingletonConfigurable): pass
  155 + class Bam(Bar): pass
  156 + self.assertEquals(Bar.initialized(), False)
  157 + self.assertEquals(Bam.initialized(), False)
  158 + bam = Bam.instance()
  159 + bam == Bar.instance()
  160 + self.assertEquals(Bar.initialized(), True)
  161 + self.assertEquals(Bam.initialized(), True)
  162 + self.assertEquals(bam, Bam._instance)
  163 + self.assertEquals(bam, Bar._instance)
  164 + self.assertEquals(SingletonConfigurable._instance, None)
28 IPython/config/tests/test_loader.py
@@ -24,9 +24,12 @@
24 24 from tempfile import mkstemp
25 25 from unittest import TestCase
26 26
  27 +from IPython.utils.traitlets import Int, Unicode
  28 +from IPython.config.configurable import Configurable
27 29 from IPython.config.loader import (
28 30 Config,
29   - PyFileConfigLoader,
  31 + PyFileConfigLoader,
  32 + KeyValueConfigLoader,
30 33 ArgParseConfigLoader,
31 34 ConfigError
32 35 )
@@ -38,11 +41,11 @@
38 41
39 42 pyfile = """
40 43 c = get_config()
41   -c.a = 10
42   -c.b = 20
43   -c.Foo.Bar.value = 10
44   -c.Foo.Bam.value = range(10)
45   -c.D.C.value = 'hi there'
  44 +c.a=10
  45 +c.b=20
  46 +c.Foo.Bar.value=10
  47 +c.Foo.Bam.value=range(10)
  48 +c.D.C.value='hi there'
46 49 """
47 50
48 51 class TestPyFileCL(TestCase):
@@ -109,6 +112,19 @@ def test_argv(self):
109 112 self.assertEquals(config.Global.bam, 'wow')
110 113
111 114
  115 +class TestKeyValueCL(TestCase):
  116 +
  117 + def test_basic(self):
  118 + cl = KeyValueConfigLoader()
  119 + argv = [s.strip('c.') for s in pyfile.split('\n')[2:-1]]
  120 + config = cl.load_config(argv)
  121 + self.assertEquals(config.a, 10)
  122 + self.assertEquals(config.b, 20)
  123 + self.assertEquals(config.Foo.Bar.value, 10)
  124 + self.assertEquals(config.Foo.Bam.value, range(10))
  125 + self.assertEquals(config.D.C.value, 'hi there')
  126 +
  127 +
112 128 class TestConfig(TestCase):
113 129
114 130 def test_setget(self):
8 IPython/core/display.py
@@ -119,4 +119,12 @@ def display_json(*objs):
119 119 display(*objs, include=['text/plain','application/json'])
120 120
121 121
  122 +def display_javascript(*objs):
  123 + """Display the Javascript representation of an object.
122 124
  125 + Parameters
  126 + ----------
  127 + objs : tuple of objects
  128 + The Python objects to display.
  129 + """
  130 + display(*objs, include=['text/plain','application/javascript'])
70 IPython/core/formatters.py
@@ -52,7 +52,8 @@ def _formatters_default(self):
52 52 SVGFormatter,
53 53 PNGFormatter,
54 54 LatexFormatter,
55   - JSONFormatter
  55 + JSONFormatter,
  56 + JavascriptFormatter
56 57 ]
57 58 d = {}
58 59 for cls in formatter_classes:
@@ -220,15 +221,14 @@ def __call__(self, obj):
220 221 obj_id = id(obj)
221 222 try:
222 223 obj_class = getattr(obj, '__class__', None) or type(obj)
223   - if hasattr(obj_class, self.print_method):
224   - printer = getattr(obj_class, self.print_method)
225   - return printer(obj)
  224 + # First try to find registered singleton printers for the type.
226 225 try:
227 226 printer = self.singleton_printers[obj_id]
228 227 except (TypeError, KeyError):
229 228 pass
230 229 else:
231 230 return printer(obj)
  231 + # Next look for type_printers.
232 232 for cls in pretty._get_mro(obj_class):
233 233 if cls in self.type_printers:
234 234 return self.type_printers[cls](obj)
@@ -236,6 +236,10 @@ def __call__(self, obj):
236 236 printer = self._in_deferred_types(cls)
237 237 if printer is not None:
238 238 return printer(obj)
  239 + # Finally look for special method names.
  240 + if hasattr(obj_class, self.print_method):
  241 + printer = getattr(obj_class, self.print_method)
  242 + return printer(obj)
239 243 return None
240 244 except Exception:
241 245 pass
@@ -339,8 +343,8 @@ def dtype_pprinter(obj, p, cycle):
339 343 # something.
340 344 enabled = Bool(True, config=False)
341 345
342   - # Look for a __pretty__ methods to use for pretty printing.
343   - print_method = Str('__pretty__')
  346 + # Look for a _repr_pretty_ methods to use for pretty printing.
  347 + print_method = Str('_repr_pretty_')
344 348
345 349 # Whether to pretty-print or not.
346 350 pprint = Bool(True, config=True)
@@ -442,66 +446,97 @@ class HTMLFormatter(BaseFormatter):
442 446 """An HTML formatter.
443 447
444 448 To define the callables that compute the HTML representation of your
445   - objects, define a :meth:`__html__` method or use the :meth:`for_type`
  449 + objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
446 450 or :meth:`for_type_by_name` methods to register functions that handle
447 451 this.
  452 +
  453 + The return value of this formatter should be a valid HTML snippet that
  454 + could be injected into an existing DOM. It should *not* include the
  455 + ```<html>`` or ```<body>`` tags.
448 456 """
449 457 format_type = Str('text/html')
450 458
451   - print_method = Str('__html__')
  459 + print_method = Str('_repr_html_')
452 460
453 461
454 462 class SVGFormatter(BaseFormatter):
455 463 """An SVG formatter.
456 464
457 465 To define the callables that compute the SVG representation of your
458   - objects, define a :meth:`__svg__` method or use the :meth:`for_type`
  466 + objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
459 467 or :meth:`for_type_by_name` methods to register functions that handle
460 468 this.
  469 +
  470 + The return value of this formatter should be valid SVG enclosed in
  471 + ```<svg>``` tags, that could be injected into an existing DOM. It should
  472 + *not* include the ```<html>`` or ```<body>`` tags.
461 473 """
462 474 format_type = Str('image/svg+xml')
463 475
464   - print_method = Str('__svg__')
  476 + print_method = Str('_repr_svg_')
465 477
466 478
467 479 class PNGFormatter(BaseFormatter):
468 480 """A PNG formatter.
469 481
470 482 To define the callables that compute the PNG representation of your
471   - objects, define a :meth:`__png__` method or use the :meth:`for_type`
  483 + objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
472 484 or :meth:`for_type_by_name` methods to register functions that handle
473   - this. The raw data should be the base64 encoded raw png data.
  485 + this.
  486 +
  487 + The return value of this formatter should be raw PNG data, *not*
  488 + base64 encoded.
474 489 """
475 490 format_type = Str('image/png')
476 491
477   - print_method = Str('__png__')
  492 + print_method = Str('_repr_png_')
478 493
479 494
480 495 class LatexFormatter(BaseFormatter):
481 496 """A LaTeX formatter.
482 497
483 498 To define the callables that compute the LaTeX representation of your
484   - objects, define a :meth:`__latex__` method or use the :meth:`for_type`
  499 + objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
485 500 or :meth:`for_type_by_name` methods to register functions that handle
486 501 this.
  502 +
  503 + The return value of this formatter should be a valid LaTeX equation,
  504 + enclosed in either ```$``` or ```$$```.
487 505 """
488 506 format_type = Str('text/latex')
489 507
490   - print_method = Str('__latex__')
  508 + print_method = Str('_repr_latex_')
491 509
492 510
493 511 class JSONFormatter(BaseFormatter):
494 512 """A JSON string formatter.
495 513
496 514 To define the callables that compute the JSON string representation of
497   - your objects, define a :meth:`__json__` method or use the :meth:`for_type`
  515 + your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
498 516 or :meth:`for_type_by_name` methods to register functions that handle
499 517 this.
  518 +
  519 + The return value of this formatter should be a valid JSON string.
500 520 """
501 521 format_type = Str('application/json')
502 522
503   - print_method = Str('__json__')
  523 + print_method = Str('_repr_json_')
  524 +
  525 +
  526 +class JavascriptFormatter(BaseFormatter):
  527 + """A Javascript formatter.
  528 +
  529 + To define the callables that compute the Javascript representation of
  530 + your objects, define a :meth:`_repr_javascript_` method or use the
  531 + :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
  532 + that handle this.
  533 +
  534 + The return value of this formatter should be valid Javascript code and
  535 + should *not* be enclosed in ```<script>``` tags.
  536 + """
  537 + format_type = Str('application/javascript')
504 538
  539 + print_method = Str('_repr_javascript_')
505 540
506 541 FormatterABC.register(BaseFormatter)
507 542 FormatterABC.register(PlainTextFormatter)
@@ -510,6 +545,7 @@ class JSONFormatter(BaseFormatter):
510 545 FormatterABC.register(PNGFormatter)
511 546 FormatterABC.register(LatexFormatter)
512 547 FormatterABC.register(JSONFormatter)
  548 +FormatterABC.register(JavascriptFormatter)
513 549
514 550
515 551 def format_display_data(obj, include=None, exclude=None):
4 IPython/core/history.py
@@ -23,7 +23,7 @@
23 23 # Our own packages
24 24 from IPython.config.configurable import Configurable
25 25
26   -from IPython.testing import decorators as testdec
  26 +from IPython.testing.skipdoctest import skip_doctest
27 27 from IPython.utils import io
28 28 from IPython.utils.traitlets import Bool, Dict, Instance, Int, List, Unicode
29 29 from IPython.utils.warn import warn
@@ -548,7 +548,7 @@ def _format_lineno(session, line):
548 548 return str(line)
549 549 return "%s#%s" % (session, line)
550 550
551   -@testdec.skip_doctest
  551 +@skip_doctest
552 552 def magic_history(self, parameter_s = ''):
553 553 """Print input history (_i<n> variables), with most recent last.
554 554
122 IPython/core/interactiveshell.py
@@ -31,7 +31,7 @@
31 31 import types
32 32 from contextlib import nested
33 33
34   -from IPython.config.configurable import Configurable
  34 +from IPython.config.configurable import SingletonConfigurable
35 35 from IPython.core import debugger, oinspect
36 36 from IPython.core import history as ipcorehist
37 37 from IPython.core import page
@@ -132,9 +132,7 @@ def validate(self, obj, value):
132 132 value = value.replace('\\n','\n')
133 133 return super(SeparateStr, self).validate(obj, value)
134 134
135   -class MultipleInstanceError(Exception):
136   - pass
137   -
  135 +
138 136 class ReadlineNoRecord(object):
139 137 """Context manager to execute some code, then reload readline history
140 138 so that interactive input to the code doesn't appear when pressing up."""
@@ -181,25 +179,78 @@ def get_readline_tail(self, n=10):
181 179 return [ghi(x) for x in range(start, end)]
182 180
183 181
  182 +_autocall_help = """
  183 +Make IPython automatically call any callable object even if
  184 +you didn't type explicit parentheses. For example, 'str 43' becomes 'str(43)'
  185 +automatically. The value can be '0' to disable the feature, '1' for 'smart'
  186 +autocall, where it is not applied if there are no more arguments on the line,
  187 +and '2' for 'full' autocall, where all callable objects are automatically
  188 +called (even if no arguments are present). The default is '1'.
  189 +"""
  190 +
184 191 #-----------------------------------------------------------------------------
185 192 # Main IPython class
186 193 #-----------------------------------------------------------------------------
187 194
188   -class InteractiveShell(Configurable, Magic):
  195 +class InteractiveShell(SingletonConfigurable, Magic):
189 196 """An enhanced, interactive shell for Python."""
190 197
191 198 _instance = None
192   - autocall = Enum((0,1,2), default_value=1, config=True)
  199 +
  200 + autocall = Enum((0,1,2), default_value=1, config=True, help=
  201 + """
  202 + Make IPython automatically call any callable object even if you didn't
  203 + type explicit parentheses. For example, 'str 43' becomes 'str(43)'
  204 + automatically. The value can be '0' to disable the feature, '1' for
  205 + 'smart' autocall, where it is not applied if there are no more
  206 + arguments on the line, and '2' for 'full' autocall, where all callable
  207 + objects are automatically called (even if no arguments are present).
  208 + The default is '1'.
  209 + """
  210 + )
193 211 # TODO: remove all autoindent logic and put into frontends.
194 212 # We can't do this yet because even runlines uses the autoindent.
195   - autoindent = CBool(True, config=True)
196   - automagic = CBool(True, config=True)
197   - cache_size = Int(1000, config=True)
198   - color_info = CBool(True, config=True)
  213 + autoindent = CBool(True, config=True, help=
  214 + """
  215 + Autoindent IPython code entered interactively.
  216 + """
  217 + )
  218 + automagic = CBool(True, config=True, help=
  219 + """
  220 + Enable magic commands to be called without the leading %.
  221 + """
  222 + )
  223 + cache_size = Int(1000, config=True, help=
  224 + """
  225 + Set the size of the output cache. The default is 1000, you can
  226 + change it permanently in your config file. Setting it to 0 completely
  227 + disables the caching system, and the minimum value accepted is 20 (if
  228 + you provide a value less than 20, it is reset to 0 and a warning is
  229 + issued). This limit is defined because otherwise you'll spend more
  230 + time re-flushing a too small cache than working
  231 + """
  232 + )
  233 + color_info = CBool(True, config=True, help=
  234 + """
  235 + Use colors for displaying information about objects. Because this
  236 + information is passed through a pager (like 'less'), and some pagers
  237 + get confused with color codes, this capability can be turned off.
  238 + """
  239 + )
199 240 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
200 241 default_value=get_default_colors(), config=True)
201 242 debug = CBool(False, config=True)
202   - deep_reload = CBool(False, config=True)
  243 + deep_reload = CBool(False, config=True, help=
  244 + """
  245 + Enable deep (recursive) reloading by default. IPython can use the
  246 + deep_reload module which reloads changes in modules recursively (it
  247 + replaces the reload() function, so you don't need to change anything to
  248 + use it). deep_reload() forces a full reload of modules whose code may
  249 + have changed, which the default reload() function does not. When
  250 + deep_reload is off, IPython will use the normal reload(), but
  251 + deep_reload will still be available as dreload().
  252 + """
  253 + )
203 254 display_formatter = Instance(DisplayFormatter)
204 255 displayhook_class = Type(DisplayHook)
205 256 display_pub_class = Type(DisplayPublisher)
@@ -217,12 +268,28 @@ def _exiter_default(self):
217 268 # interactive statements or whole blocks.
218 269 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
219 270 (), {})
220   - logstart = CBool(False, config=True)
221   - logfile = Unicode('', config=True)
222   - logappend = Unicode('', config=True)
  271 + logstart = CBool(False, config=True, help=
  272 + """
  273 + Start logging to the default log file.
  274 + """
  275 + )
  276 + logfile = Unicode('', config=True, help=
  277 + """
  278 + The name of the logfile to use.
  279 + """
  280 + )
  281 + logappend = Unicode('', config=True, help=
  282 + """
  283 + Start logging to the given file in append mode.
  284 + """
  285 + )
223 286 object_info_string_level = Enum((0,1,2), default_value=0,
224 287 config=True)
225   - pdb = CBool(False, config=True)
  288 + pdb = CBool(False, config=True, help=
  289 + """
  290 + Automatically call the pdb debugger after every exception.
  291 + """
  292 + )
226 293
227 294 profile = Unicode('', config=True)
228 295 prompt_in1 = Str('In [\\#]: ', config=True)
@@ -356,31 +423,6 @@ def __init__(self, config=None, ipython_dir=None,
356 423 self.hooks.late_startup_hook()
357 424 atexit.register(self.atexit_operations)
358 425
359   - @classmethod
360   - def instance(cls, *args, **kwargs):
361   - """Returns a global InteractiveShell instance."""
362   - if cls._instance is None:
363   - inst = cls(*args, **kwargs)
364   - # Now make sure that the instance will also be returned by
365   - # the subclasses instance attribute.
366   - for subclass in cls.mro():
367   - if issubclass(cls, subclass) and \
368   - issubclass(subclass, InteractiveShell):
369   - subclass._instance = inst
370   - else:
371   - break