Permalink
Browse files

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

…nd-logging
  • Loading branch information...
2 parents 85382a1 + 5cd1ae1 commit 5809ebf294b4615e555d09e29b4964212da29232 @omazapa omazapa committed May 26, 2011
Showing with 2,502 additions and 870 deletions.
  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
@@ -0,0 +1,230 @@
+# encoding: utf-8
+"""
+A base class for a configurable application.
+
+Authors:
+
+* Brian Granger
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (C) 2008-2011 The IPython Development Team
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# Imports
+#-----------------------------------------------------------------------------
+
+from copy import deepcopy
+import logging
+import sys
+
+from IPython.config.configurable import SingletonConfigurable
+from IPython.config.loader import (
+ KeyValueConfigLoader, PyFileConfigLoader, Config
+)
+
+from IPython.utils.traitlets import (
+ Unicode, List, Int, Enum, Dict
+)
+from IPython.utils.text import indent
+
+#-----------------------------------------------------------------------------
+# Descriptions for
+#-----------------------------------------------------------------------------
+
+flag_description = """
+Flags are command-line arguments passed as '--<flag>'.
+These take no parameters, unlike regular key-value arguments.
+They are typically used for setting boolean flags, or enabling
+modes that involve setting multiple options together.
+""".strip() # trim newlines of front and back
+
+alias_description = """
+These are commonly set parameters, given abbreviated aliases for convenience.
+They are set in the same `name=value` way as class parameters, where
+<name> is replaced by the real parameter for which it is an alias.
+""".strip() # trim newlines of front and back
+
+keyvalue_description = """
+Parameters are set from command-line arguments of the form:
+`Class.trait=value`. Parameters will *never* be prefixed with '-'.
+This line is evaluated in Python, so simple expressions are allowed, e.g.
+ `C.a='range(3)'` For setting C.a=[0,1,2]
+""".strip() # trim newlines of front and back
+
+#-----------------------------------------------------------------------------
+# Application class
+#-----------------------------------------------------------------------------
+
+
+class Application(SingletonConfigurable):
+ """A singleton application with full configuration support."""
+
+ # The name of the application, will usually match the name of the command
+ # line application
+ app_name = Unicode(u'application')
+
+ # The description of the application that is printed at the beginning
+ # of the help.
+ description = Unicode(u'This is an application.')
+ # default section descriptions
+ flag_description = Unicode(flag_description)
+ alias_description = Unicode(alias_description)
+ keyvalue_description = Unicode(keyvalue_description)
+
+
+ # A sequence of Configurable subclasses whose config=True attributes will
+ # be exposed at the command line.
+ classes = List([])
+
+ # The version string of this application.
+ version = Unicode(u'0.0')
+
+ # The log level for the application
+ log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
+ config=True,
+ help="Set the log level (0,10,20,30,40,50).")
+
+ # the alias map for configurables
+ aliases = Dict(dict(log_level='Application.log_level'))
+
+ # flags for loading Configurables or store_const style flags
+ # flags are loaded from this dict by '--key' flags
+ # this must be a dict of two-tuples, the first element being the Config/dict
+ # and the second being the help string for the flag
+ flags = Dict()
+
+
+ def __init__(self, **kwargs):
+ SingletonConfigurable.__init__(self, **kwargs)
+ # Add my class to self.classes so my attributes appear in command line
+ # options.
+ self.classes.insert(0, self.__class__)
+
+ # ensure self.flags dict is valid
+ for key,value in self.flags.iteritems():
+ assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
+ assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
+ assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
+ self.init_logging()
+
+ def init_logging(self):
+ """Start logging for this application.
+
+ The default is to log to stdout using a StreaHandler. The log level
+ starts at loggin.WARN, but this can be adjusted by setting the
+ ``log_level`` attribute.
+ """
+ self.log = logging.getLogger(self.__class__.__name__)
+ self.log.setLevel(self.log_level)
+ self._log_handler = logging.StreamHandler()
+ self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
+ self._log_handler.setFormatter(self._log_formatter)
+ self.log.addHandler(self._log_handler)
+
+ def _log_level_changed(self, name, old, new):
+ """Adjust the log level when log_level is set."""
+ self.log.setLevel(new)
+
+ def print_alias_help(self):
+ """print the alias part of the help"""
+ if not self.aliases:
+ return
+
+ print "Aliases"
+ print "-------"
+ print self.alias_description
+ print
+
+ classdict = {}
+ for c in self.classes:
+ classdict[c.__name__] = c
+
+ for alias, longname in self.aliases.iteritems():
+ classname, traitname = longname.split('.',1)
+ cls = classdict[classname]
+
+ trait = cls.class_traits(config=True)[traitname]
+ help = trait.get_metadata('help')
+ print alias, "(%s)"%longname, ':', trait.__class__.__name__
+ if help:
+ print indent(help)
+ print
+
+ def print_flag_help(self):
+ """print the flag part of the help"""
+ if not self.flags:
+ return
+
+ print "Flags"
+ print "-----"
+ print self.flag_description
+ print
+
+ for m, (cfg,help) in self.flags.iteritems():
+ print '--'+m
+ print indent(help)
+ print
+
+ def print_help(self):
+ """Print the help for each Configurable class in self.classes."""
+ self.print_flag_help()
+ self.print_alias_help()
+ if self.classes:
+ print "Class parameters"
+ print "----------------"
+ print self.keyvalue_description
+ print
+
+ for cls in self.classes:
+ cls.class_print_help()
+ print
+
+ def print_description(self):
+ """Print the application description."""
+ print self.description
+ print
+
+ def print_version(self):
+ """Print the version string."""
+ print self.version
+
+ def update_config(self, config):
+ """Fire the traits events when the config is updated."""
+ # Save a copy of the current config.
+ newconfig = deepcopy(self.config)
+ # Merge the new config into the current one.
+ newconfig._merge(config)
+ # Save the combined config as self.config, which triggers the traits
+ # events.
+ self.config = config
+
+ def parse_command_line(self, argv=None):
+ """Parse the command line arguments."""
+ argv = sys.argv[1:] if argv is None else argv
+
+ if '-h' in argv or '--help' in argv:
+ self.print_description()
+ self.print_help()
+ sys.exit(1)
+
+ if '--version' in argv:
+ self.print_version()
+ sys.exit(1)
+
+ loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
+ flags=self.flags)
+ config = loader.load_config()
+ self.update_config(config)
+
+ def load_config_file(self, filename, path=None):
+ """Load a .py based config file by filename and path."""
+ # TODO: this raises IOError if filename does not exist.
+ loader = PyFileConfigLoader(filename, path=path)
+ config = loader.load_config()
+ self.update_config(config)
+
@@ -22,11 +22,10 @@
from copy import deepcopy
import datetime
-from weakref import WeakValueDictionary
-from IPython.utils.importstring import import_item
from loader import Config
from IPython.utils.traitlets import HasTraits, Instance
+from IPython.utils.text import indent
#-----------------------------------------------------------------------------
@@ -38,6 +37,9 @@ class ConfigurableError(Exception):
pass
+class MultipleInstanceError(ConfigurableError):
+ pass
+
#-----------------------------------------------------------------------------
# Configurable implementation
#-----------------------------------------------------------------------------
@@ -137,3 +139,87 @@ def _config_changed(self, name, old, new):
# shared by all instances, effectively making it a class attribute.
setattr(self, k, deepcopy(config_value))
+ @classmethod
+ def class_get_help(cls):
+ """Get the help string for this class in ReST format."""
+ cls_traits = cls.class_traits(config=True)
+ final_help = []
+ final_help.append(u'%s options' % cls.__name__)
+ final_help.append(len(final_help[0])*u'-')
+ for k, v in cls_traits.items():
+ help = v.get_metadata('help')
+ header = "%s.%s : %s" % (cls.__name__, k, v.__class__.__name__)
+ final_help.append(header)
+ if help is not None:
+ final_help.append(indent(help))
+ return '\n'.join(final_help)
+
+ @classmethod
+ def class_print_help(cls):
+ print cls.class_get_help()
+
+
+class SingletonConfigurable(Configurable):
+ """A configurable that only allows one instance.
+
+ This class is for classes that should only have one instance of itself
+ or *any* subclass. To create and retrieve such a class use the
+ :meth:`SingletonConfigurable.instance` method.
+ """
+
+ _instance = None
+
+ @classmethod
+ def instance(cls, *args, **kwargs):
+ """Returns a global instance of this class.
+
+ This method create a new instance if none have previously been created
+ and returns a previously created instance is one already exists.
+
+ The arguments and keyword arguments passed to this method are passed
+ on to the :meth:`__init__` method of the class upon instantiation.
+
+ Examples
+ --------
+
+ Create a singleton class using instance, and retrieve it::
+
+ >>> from IPython.config.configurable import SingletonConfigurable
+ >>> class Foo(SingletonConfigurable): pass
+ >>> foo = Foo.instance()
+ >>> foo == Foo.instance()
+ True
+
+ Create a subclass that is retrived using the base class instance::
+
+ >>> class Bar(SingletonConfigurable): pass
+ >>> class Bam(Bar): pass
+ >>> bam = Bam.instance()
+ >>> bam == Bar.instance()
+ True
+ """
+ # Create and save the instance
+ if cls._instance is None:
+ inst = cls(*args, **kwargs)
+ # Now make sure that the instance will also be returned by
+ # the subclasses instance attribute.
+ for subclass in cls.mro():
+ if issubclass(cls, subclass) and \
+ issubclass(subclass, SingletonConfigurable) and \
+ subclass != SingletonConfigurable:
+ subclass._instance = inst
+ else:
+ break
+ if isinstance(cls._instance, cls):
+ return cls._instance
+ else:
+ raise MultipleInstanceError(
+ 'Multiple incompatible subclass instances of '
+ '%s are being created.' % cls.__name__
+ )
+
+ @classmethod
+ def initialized(cls):
+ """Has an instance been created?"""
+ return hasattr(cls, "_instance") and cls._instance is not None
+
Oops, something went wrong.

0 comments on commit 5809ebf

Please sign in to comment.