aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/traitlets
diff options
context:
space:
mode:
authorMikhail Borisov <borisov.mikhail@gmail.com>2022-02-10 16:45:39 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:45:39 +0300
commita6a92afe03e02795227d2641b49819b687f088f8 (patch)
treef6984a1d27d5a7ec88a6fdd6e20cd5b7693b6ece /contrib/python/traitlets
parentc6dc8b8bd530985bc4cce0137e9a5de32f1087cb (diff)
downloadydb-a6a92afe03e02795227d2641b49819b687f088f8.tar.gz
Restoring authorship annotation for Mikhail Borisov <borisov.mikhail@gmail.com>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/traitlets')
-rw-r--r--contrib/python/traitlets/py2/COPYING.md124
-rw-r--r--contrib/python/traitlets/py2/traitlets/__init__.py6
-rw-r--r--contrib/python/traitlets/py2/traitlets/_version.py2
-rw-r--r--contrib/python/traitlets/py2/traitlets/config/__init__.py16
-rw-r--r--contrib/python/traitlets/py2/traitlets/config/application.py1184
-rw-r--r--contrib/python/traitlets/py2/traitlets/config/configurable.py794
-rw-r--r--contrib/python/traitlets/py2/traitlets/config/loader.py1640
-rw-r--r--contrib/python/traitlets/py2/traitlets/config/manager.py174
-rw-r--r--contrib/python/traitlets/py2/traitlets/log.py42
-rw-r--r--contrib/python/traitlets/py2/traitlets/traitlets.py4370
-rw-r--r--contrib/python/traitlets/py2/traitlets/utils/getargspec.py170
-rw-r--r--contrib/python/traitlets/py2/traitlets/utils/importstring.py80
-rw-r--r--contrib/python/traitlets/py2/traitlets/utils/sentinel.py34
-rw-r--r--contrib/python/traitlets/py2/ya.make46
-rw-r--r--contrib/python/traitlets/py3/COPYING.md124
-rw-r--r--contrib/python/traitlets/py3/traitlets/__init__.py6
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/__init__.py16
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/application.py920
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/configurable.py694
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/loader.py1134
-rw-r--r--contrib/python/traitlets/py3/traitlets/config/manager.py166
-rw-r--r--contrib/python/traitlets/py3/traitlets/log.py42
-rw-r--r--contrib/python/traitlets/py3/traitlets/traitlets.py3822
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/getargspec.py40
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/importstring.py70
-rw-r--r--contrib/python/traitlets/py3/traitlets/utils/sentinel.py30
-rw-r--r--contrib/python/traitlets/py3/ya.make42
-rw-r--r--contrib/python/traitlets/ya.make4
28 files changed, 7896 insertions, 7896 deletions
diff --git a/contrib/python/traitlets/py2/COPYING.md b/contrib/python/traitlets/py2/COPYING.md
index 39ca730a63..e314a9d376 100644
--- a/contrib/python/traitlets/py2/COPYING.md
+++ b/contrib/python/traitlets/py2/COPYING.md
@@ -1,62 +1,62 @@
-# Licensing terms
-
-Traitlets is adapted from enthought.traits, Copyright (c) Enthought, Inc.,
-under the terms of the Modified BSD License.
-
-This project is licensed under the terms of the Modified BSD License
-(also known as New or Revised or 3-Clause BSD), as follows:
-
-- Copyright (c) 2001-, IPython Development Team
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
-
-Neither the name of the IPython Development Team nor the names of its
-contributors may be used to endorse or promote products derived from this
-software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-## About the IPython Development Team
-
-The IPython Development Team is the set of all contributors to the IPython project.
-This includes all of the IPython subprojects.
-
-The core team that coordinates development on GitHub can be found here:
-https://github.com/jupyter/.
-
-## Our Copyright Policy
-
-IPython uses a shared copyright model. Each contributor maintains copyright
-over their contributions to IPython. But, it is important to note that these
-contributions are typically only changes to the repositories. Thus, the IPython
-source code, in its entirety is not the copyright of any single person or
-institution. Instead, it is the collective copyright of the entire IPython
-Development Team. If individual contributors want to maintain a record of what
-changes/contributions they have specific copyright on, they should indicate
-their copyright in the commit message of the change, when they commit the
-change to one of the IPython repositories.
-
-With this in mind, the following banner should be used in any source code file
-to indicate the copyright and license terms:
-
- # Copyright (c) IPython Development Team.
- # Distributed under the terms of the Modified BSD License.
+# Licensing terms
+
+Traitlets is adapted from enthought.traits, Copyright (c) Enthought, Inc.,
+under the terms of the Modified BSD License.
+
+This project is licensed under the terms of the Modified BSD License
+(also known as New or Revised or 3-Clause BSD), as follows:
+
+- Copyright (c) 2001-, IPython Development Team
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+Neither the name of the IPython Development Team nor the names of its
+contributors may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+## About the IPython Development Team
+
+The IPython Development Team is the set of all contributors to the IPython project.
+This includes all of the IPython subprojects.
+
+The core team that coordinates development on GitHub can be found here:
+https://github.com/jupyter/.
+
+## Our Copyright Policy
+
+IPython uses a shared copyright model. Each contributor maintains copyright
+over their contributions to IPython. But, it is important to note that these
+contributions are typically only changes to the repositories. Thus, the IPython
+source code, in its entirety is not the copyright of any single person or
+institution. Instead, it is the collective copyright of the entire IPython
+Development Team. If individual contributors want to maintain a record of what
+changes/contributions they have specific copyright on, they should indicate
+their copyright in the commit message of the change, when they commit the
+change to one of the IPython repositories.
+
+With this in mind, the following banner should be used in any source code file
+to indicate the copyright and license terms:
+
+ # Copyright (c) IPython Development Team.
+ # Distributed under the terms of the Modified BSD License.
diff --git a/contrib/python/traitlets/py2/traitlets/__init__.py b/contrib/python/traitlets/py2/traitlets/__init__.py
index b609adb565..39933e5a79 100644
--- a/contrib/python/traitlets/py2/traitlets/__init__.py
+++ b/contrib/python/traitlets/py2/traitlets/__init__.py
@@ -1,3 +1,3 @@
-from .traitlets import *
-from .utils.importstring import import_item
-from ._version import version_info, __version__
+from .traitlets import *
+from .utils.importstring import import_item
+from ._version import version_info, __version__
diff --git a/contrib/python/traitlets/py2/traitlets/_version.py b/contrib/python/traitlets/py2/traitlets/_version.py
index ed16b3c1e1..6cc5c82a4f 100644
--- a/contrib/python/traitlets/py2/traitlets/_version.py
+++ b/contrib/python/traitlets/py2/traitlets/_version.py
@@ -1,2 +1,2 @@
version_info = (4, 3, 3)
-__version__ = '.'.join(map(str, version_info))
+__version__ = '.'.join(map(str, version_info))
diff --git a/contrib/python/traitlets/py2/traitlets/config/__init__.py b/contrib/python/traitlets/py2/traitlets/config/__init__.py
index 0ae7d63171..1531ee5930 100644
--- a/contrib/python/traitlets/py2/traitlets/config/__init__.py
+++ b/contrib/python/traitlets/py2/traitlets/config/__init__.py
@@ -1,8 +1,8 @@
-# encoding: utf-8
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-from .application import *
-from .configurable import *
-from .loader import Config
+# encoding: utf-8
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+from .application import *
+from .configurable import *
+from .loader import Config
diff --git a/contrib/python/traitlets/py2/traitlets/config/application.py b/contrib/python/traitlets/py2/traitlets/config/application.py
index d3a4c45e77..c0467e6c48 100644
--- a/contrib/python/traitlets/py2/traitlets/config/application.py
+++ b/contrib/python/traitlets/py2/traitlets/config/application.py
@@ -1,68 +1,68 @@
-# encoding: utf-8
-"""A base class for a configurable application."""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-from __future__ import print_function
-
+# encoding: utf-8
+"""A base class for a configurable application."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import print_function
+
from copy import deepcopy
-import json
-import logging
-import os
-import re
-import sys
+import json
+import logging
+import os
+import re
+import sys
from collections import defaultdict, OrderedDict
-
-from decorator import decorator
-
-from traitlets.config.configurable import Configurable, SingletonConfigurable
-from traitlets.config.loader import (
- KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, JSONFileConfigLoader
-)
-
-from traitlets.traitlets import (
+
+from decorator import decorator
+
+from traitlets.config.configurable import Configurable, SingletonConfigurable
+from traitlets.config.loader import (
+ KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, JSONFileConfigLoader
+)
+
+from traitlets.traitlets import (
Bool, Unicode, List, Enum, Dict, Instance, TraitError, observe, observe_compat, default,
-)
-from ipython_genutils.importstring import import_item
-from ipython_genutils.text import indent, wrap_paragraphs, dedent
-from ipython_genutils import py3compat
-
+)
+from ipython_genutils.importstring import import_item
+from ipython_genutils.text import indent, wrap_paragraphs, dedent
+from ipython_genutils import py3compat
+
import six
-#-----------------------------------------------------------------------------
-# Descriptions for the various sections
-#-----------------------------------------------------------------------------
-
-# merge flags&aliases into options
-option_description = """
-Arguments that take values are actually convenience aliases to full
-Configurables, whose aliases are listed on the help line. For more information
-on full configurables, see '--help-all'.
-""".strip() # trim newlines of front and back
-
-keyvalue_description = """
-Parameters are set from command-line arguments of the form:
-`--Class.trait=value`.
-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
-
-# sys.argv can be missing, for example when python is embedded. See the docs
-# for details: http://docs.python.org/2/c-api/intro.html#embedding-python
-if not hasattr(sys, "argv"):
- sys.argv = [""]
-
-subcommand_description = """
-Subcommands are launched as `{app} cmd [args]`. For information on using
-subcommand 'cmd', do: `{app} cmd -h`.
-"""
-# get running program name
-
-#-----------------------------------------------------------------------------
-# Application class
-#-----------------------------------------------------------------------------
-
+#-----------------------------------------------------------------------------
+# Descriptions for the various sections
+#-----------------------------------------------------------------------------
+
+# merge flags&aliases into options
+option_description = """
+Arguments that take values are actually convenience aliases to full
+Configurables, whose aliases are listed on the help line. For more information
+on full configurables, see '--help-all'.
+""".strip() # trim newlines of front and back
+
+keyvalue_description = """
+Parameters are set from command-line arguments of the form:
+`--Class.trait=value`.
+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
+
+# sys.argv can be missing, for example when python is embedded. See the docs
+# for details: http://docs.python.org/2/c-api/intro.html#embedding-python
+if not hasattr(sys, "argv"):
+ sys.argv = [""]
+
+subcommand_description = """
+Subcommands are launched as `{app} cmd [args]`. For information on using
+subcommand 'cmd', do: `{app} cmd -h`.
+"""
+# get running program name
+
+#-----------------------------------------------------------------------------
+# Application class
+#-----------------------------------------------------------------------------
+
_envvar = os.environ.get('TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR','')
@@ -74,194 +74,194 @@ else:
raise ValueError("Unsupported value for environment variable: 'TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
-@decorator
-def catch_config_error(method, app, *args, **kwargs):
- """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
-
- On a TraitError (generally caused by bad config), this will print the trait's
- message, and exit the app.
-
- For use on init methods, to prevent invoking excepthook on invalid input.
- """
- try:
- return method(app, *args, **kwargs)
- except (TraitError, ArgumentError) as e:
- app.print_help()
- app.log.fatal("Bad config encountered during initialization:")
- app.log.fatal(str(e))
- app.log.debug("Config at the time: %s", app.config)
- app.exit(1)
-
-
-class ApplicationError(Exception):
- pass
-
-
-class LevelFormatter(logging.Formatter):
- """Formatter with additional `highlevel` record
-
- This field is empty if log level is less than highlevel_limit,
- otherwise it is formatted with self.highlevel_format.
-
- Useful for adding 'WARNING' to warning messages,
- without adding 'INFO' to info, etc.
- """
- highlevel_limit = logging.WARN
- highlevel_format = " %(levelname)s |"
-
- def format(self, record):
- if record.levelno >= self.highlevel_limit:
- record.highlevel = self.highlevel_format % record.__dict__
- else:
- record.highlevel = ""
- return super(LevelFormatter, self).format(record)
-
-
-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
- 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
- option_description = Unicode(option_description)
- keyvalue_description = Unicode(keyvalue_description)
- subcommand_description = Unicode(subcommand_description)
-
- python_config_loader_class = PyFileConfigLoader
- json_config_loader_class = JSONFileConfigLoader
-
- # The usage and example string that goes at the end of the help string.
- examples = Unicode()
-
- # A sequence of Configurable subclasses whose config=True attributes will
- # be exposed at the command line.
- classes = []
-
- def _classes_inc_parents(self):
- """Iterate through configurable classes, including configurable parents
-
- Children should always be after parents, and each class should only be
- yielded once.
- """
- seen = set()
- for c in self.classes:
- # We want to sort parents before children, so we reverse the MRO
- for parent in reversed(c.mro()):
- if issubclass(parent, Configurable) and (parent not in seen):
- seen.add(parent)
- yield parent
-
- # The version string of this application.
- version = Unicode(u'0.0')
-
- # the argv used to initialize the application
- argv = List()
-
+@decorator
+def catch_config_error(method, app, *args, **kwargs):
+ """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
+
+ On a TraitError (generally caused by bad config), this will print the trait's
+ message, and exit the app.
+
+ For use on init methods, to prevent invoking excepthook on invalid input.
+ """
+ try:
+ return method(app, *args, **kwargs)
+ except (TraitError, ArgumentError) as e:
+ app.print_help()
+ app.log.fatal("Bad config encountered during initialization:")
+ app.log.fatal(str(e))
+ app.log.debug("Config at the time: %s", app.config)
+ app.exit(1)
+
+
+class ApplicationError(Exception):
+ pass
+
+
+class LevelFormatter(logging.Formatter):
+ """Formatter with additional `highlevel` record
+
+ This field is empty if log level is less than highlevel_limit,
+ otherwise it is formatted with self.highlevel_format.
+
+ Useful for adding 'WARNING' to warning messages,
+ without adding 'INFO' to info, etc.
+ """
+ highlevel_limit = logging.WARN
+ highlevel_format = " %(levelname)s |"
+
+ def format(self, record):
+ if record.levelno >= self.highlevel_limit:
+ record.highlevel = self.highlevel_format % record.__dict__
+ else:
+ record.highlevel = ""
+ return super(LevelFormatter, self).format(record)
+
+
+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
+ 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
+ option_description = Unicode(option_description)
+ keyvalue_description = Unicode(keyvalue_description)
+ subcommand_description = Unicode(subcommand_description)
+
+ python_config_loader_class = PyFileConfigLoader
+ json_config_loader_class = JSONFileConfigLoader
+
+ # The usage and example string that goes at the end of the help string.
+ examples = Unicode()
+
+ # A sequence of Configurable subclasses whose config=True attributes will
+ # be exposed at the command line.
+ classes = []
+
+ def _classes_inc_parents(self):
+ """Iterate through configurable classes, including configurable parents
+
+ Children should always be after parents, and each class should only be
+ yielded once.
+ """
+ seen = set()
+ for c in self.classes:
+ # We want to sort parents before children, so we reverse the MRO
+ for parent in reversed(c.mro()):
+ if issubclass(parent, Configurable) and (parent not in seen):
+ seen.add(parent)
+ yield parent
+
+ # The version string of this application.
+ version = Unicode(u'0.0')
+
+ # the argv used to initialize the application
+ argv = List()
+
# Whether failing to load config files should prevent startup
raise_config_file_errors = Bool(TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR)
- # The log level for the application
- log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
- default_value=logging.WARN,
- help="Set the log level by value or name.").tag(config=True)
-
- @observe('log_level')
- @observe_compat
- def _log_level_changed(self, change):
- """Adjust the log level when log_level is set."""
+ # The log level for the application
+ log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
+ default_value=logging.WARN,
+ help="Set the log level by value or name.").tag(config=True)
+
+ @observe('log_level')
+ @observe_compat
+ def _log_level_changed(self, change):
+ """Adjust the log level when log_level is set."""
new = change.new
if isinstance(new, six.string_types):
- new = getattr(logging, new)
- self.log_level = new
- self.log.setLevel(new)
+ new = getattr(logging, new)
+ self.log_level = new
+ self.log.setLevel(new)
- _log_formatter_cls = LevelFormatter
+ _log_formatter_cls = LevelFormatter
log_datefmt = Unicode("%Y-%m-%d %H:%M:%S",
- help="The date format used by logging formatters for %(asctime)s"
- ).tag(config=True)
-
- log_format = Unicode("[%(name)s]%(highlevel)s %(message)s",
- help="The Logging format template",
- ).tag(config=True)
-
- @observe('log_datefmt', 'log_format')
- @observe_compat
- def _log_format_changed(self, change):
- """Change the log formatter when log_format is set."""
- _log_handler = self.log.handlers[0]
- _log_formatter = self._log_formatter_cls(fmt=self.log_format, datefmt=self.log_datefmt)
- _log_handler.setFormatter(_log_formatter)
-
- @default('log')
- def _log_default(self):
- """Start logging for this application.
-
- The default is to log to stderr using a StreamHandler, if no default
- handler already exists. The log level starts at logging.WARN, but this
- can be adjusted by setting the ``log_level`` attribute.
- """
- log = logging.getLogger(self.__class__.__name__)
- log.setLevel(self.log_level)
- log.propagate = False
- _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
- while _log:
- if _log.handlers:
- return log
- if not _log.propagate:
- break
- else:
- _log = _log.parent
+ help="The date format used by logging formatters for %(asctime)s"
+ ).tag(config=True)
+
+ log_format = Unicode("[%(name)s]%(highlevel)s %(message)s",
+ help="The Logging format template",
+ ).tag(config=True)
+
+ @observe('log_datefmt', 'log_format')
+ @observe_compat
+ def _log_format_changed(self, change):
+ """Change the log formatter when log_format is set."""
+ _log_handler = self.log.handlers[0]
+ _log_formatter = self._log_formatter_cls(fmt=self.log_format, datefmt=self.log_datefmt)
+ _log_handler.setFormatter(_log_formatter)
+
+ @default('log')
+ def _log_default(self):
+ """Start logging for this application.
+
+ The default is to log to stderr using a StreamHandler, if no default
+ handler already exists. The log level starts at logging.WARN, but this
+ can be adjusted by setting the ``log_level`` attribute.
+ """
+ log = logging.getLogger(self.__class__.__name__)
+ log.setLevel(self.log_level)
+ log.propagate = False
+ _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
+ while _log:
+ if _log.handlers:
+ return log
+ if not _log.propagate:
+ break
+ else:
+ _log = _log.parent
if sys.executable and sys.executable.endswith('pythonw.exe'):
- # this should really go to a file, but file-logging is only
- # hooked up in parallel applications
- _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
- else:
- _log_handler = logging.StreamHandler()
- _log_formatter = self._log_formatter_cls(fmt=self.log_format, datefmt=self.log_datefmt)
- _log_handler.setFormatter(_log_formatter)
- log.addHandler(_log_handler)
- return log
-
- # the alias map for configurables
- aliases = 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()
- @observe('flags')
- @observe_compat
- def _flags_changed(self, change):
- """ensure flags dict is valid"""
+ # this should really go to a file, but file-logging is only
+ # hooked up in parallel applications
+ _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
+ else:
+ _log_handler = logging.StreamHandler()
+ _log_formatter = self._log_formatter_cls(fmt=self.log_format, datefmt=self.log_datefmt)
+ _log_handler.setFormatter(_log_formatter)
+ log.addHandler(_log_handler)
+ return log
+
+ # the alias map for configurables
+ aliases = 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()
+ @observe('flags')
+ @observe_compat
+ def _flags_changed(self, change):
+ """ensure flags dict is valid"""
new = change.new
- for key, value in new.items():
- assert len(value) == 2, "Bad flag: %r:%s" % (key, value)
- assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s" % (key, value)
+ for key, value in new.items():
+ 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], six.string_types), "Bad flag: %r:%s" % (key, value)
-
-
- # subcommands for launching other applications
- # if this is not empty, this will be a parent Application
- # this must be a dict of two-tuples,
- # the first element being the application class/import string
- # and the second being the help string for the subcommand
- subcommands = Dict()
- # parse_command_line will initialize a subapp, if requested
- subapp = Instance('traitlets.config.application.Application', allow_none=True)
-
- # extra command-line arguments that don't set config values
- extra_args = List(Unicode())
-
+
+
+ # subcommands for launching other applications
+ # if this is not empty, this will be a parent Application
+ # this must be a dict of two-tuples,
+ # the first element being the application class/import string
+ # and the second being the help string for the subcommand
+ subcommands = Dict()
+ # parse_command_line will initialize a subapp, if requested
+ subapp = Instance('traitlets.config.application.Application', allow_none=True)
+
+ # extra command-line arguments that don't set config values
+ extra_args = List(Unicode())
+
cli_config = Instance(Config, (), {},
help="""The subset of our configuration that came from the command-line
-
+
We re-load this configuration after loading config files,
to ensure that it maintains highest priority.
"""
@@ -269,10 +269,10 @@ class Application(SingletonConfigurable):
_loaded_config_files = List()
- def __init__(self, **kwargs):
- SingletonConfigurable.__init__(self, **kwargs)
- # Ensure my class is in self.classes, so my attributes appear in command line
- # options and config files.
+ def __init__(self, **kwargs):
+ SingletonConfigurable.__init__(self, **kwargs)
+ # Ensure my class is in self.classes, so my attributes appear in command line
+ # options and config files.
cls = self.__class__
if cls not in self.classes:
if self.classes is cls.classes:
@@ -281,302 +281,302 @@ class Application(SingletonConfigurable):
else:
self.classes.insert(0, self.__class__)
- @observe('config')
- @observe_compat
- def _config_changed(self, change):
- super(Application, self)._config_changed(change)
- self.log.debug('Config changed:')
+ @observe('config')
+ @observe_compat
+ def _config_changed(self, change):
+ super(Application, self)._config_changed(change)
+ self.log.debug('Config changed:')
self.log.debug(repr(change.new))
-
- @catch_config_error
- def initialize(self, argv=None):
- """Do the basic steps to configure me.
-
- Override in subclasses.
- """
- self.parse_command_line(argv)
-
-
- def start(self):
- """Start the app mainloop.
-
- Override in subclasses.
- """
- if self.subapp is not None:
- return self.subapp.start()
-
- def print_alias_help(self):
- """Print the alias part of the help."""
- if not self.aliases:
- return
-
- lines = []
- classdict = {}
- for cls in self.classes:
- # include all parents (up to, but excluding Configurable) in available names
- for c in cls.mro()[:-3]:
- classdict[c.__name__] = c
-
+
+ @catch_config_error
+ def initialize(self, argv=None):
+ """Do the basic steps to configure me.
+
+ Override in subclasses.
+ """
+ self.parse_command_line(argv)
+
+
+ def start(self):
+ """Start the app mainloop.
+
+ Override in subclasses.
+ """
+ if self.subapp is not None:
+ return self.subapp.start()
+
+ def print_alias_help(self):
+ """Print the alias part of the help."""
+ if not self.aliases:
+ return
+
+ lines = []
+ classdict = {}
+ for cls in self.classes:
+ # include all parents (up to, but excluding Configurable) in available names
+ for c in cls.mro()[:-3]:
+ classdict[c.__name__] = c
+
for alias, longname in self.aliases.items():
- classname, traitname = longname.split('.',1)
- cls = classdict[classname]
-
- trait = cls.class_traits(config=True)[traitname]
- help = cls.class_get_trait_help(trait).splitlines()
- # reformat first line
- help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
- if len(alias) == 1:
- help[0] = help[0].replace('--%s='%alias, '-%s '%alias)
- lines.extend(help)
- # lines.append('')
- print(os.linesep.join(lines))
-
- def print_flag_help(self):
- """Print the flag part of the help."""
- if not self.flags:
- return
-
- lines = []
+ classname, traitname = longname.split('.',1)
+ cls = classdict[classname]
+
+ trait = cls.class_traits(config=True)[traitname]
+ help = cls.class_get_trait_help(trait).splitlines()
+ # reformat first line
+ help[0] = help[0].replace(longname, alias) + ' (%s)'%longname
+ if len(alias) == 1:
+ help[0] = help[0].replace('--%s='%alias, '-%s '%alias)
+ lines.extend(help)
+ # lines.append('')
+ print(os.linesep.join(lines))
+
+ def print_flag_help(self):
+ """Print the flag part of the help."""
+ if not self.flags:
+ return
+
+ lines = []
for m, (cfg,help) in self.flags.items():
- prefix = '--' if len(m) > 1 else '-'
- lines.append(prefix+m)
- lines.append(indent(dedent(help.strip())))
- # lines.append('')
- print(os.linesep.join(lines))
-
- def print_options(self):
- if not self.flags and not self.aliases:
- return
- lines = ['Options']
- lines.append('-'*len(lines[0]))
- lines.append('')
- for p in wrap_paragraphs(self.option_description):
- lines.append(p)
- lines.append('')
- print(os.linesep.join(lines))
- self.print_flag_help()
- self.print_alias_help()
- print()
-
- def print_subcommands(self):
- """Print the subcommand part of the help."""
- if not self.subcommands:
- return
-
- lines = ["Subcommands"]
- lines.append('-'*len(lines[0]))
- lines.append('')
- for p in wrap_paragraphs(self.subcommand_description.format(
- app=self.name)):
- lines.append(p)
- lines.append('')
+ prefix = '--' if len(m) > 1 else '-'
+ lines.append(prefix+m)
+ lines.append(indent(dedent(help.strip())))
+ # lines.append('')
+ print(os.linesep.join(lines))
+
+ def print_options(self):
+ if not self.flags and not self.aliases:
+ return
+ lines = ['Options']
+ lines.append('-'*len(lines[0]))
+ lines.append('')
+ for p in wrap_paragraphs(self.option_description):
+ lines.append(p)
+ lines.append('')
+ print(os.linesep.join(lines))
+ self.print_flag_help()
+ self.print_alias_help()
+ print()
+
+ def print_subcommands(self):
+ """Print the subcommand part of the help."""
+ if not self.subcommands:
+ return
+
+ lines = ["Subcommands"]
+ lines.append('-'*len(lines[0]))
+ lines.append('')
+ for p in wrap_paragraphs(self.subcommand_description.format(
+ app=self.name)):
+ lines.append(p)
+ lines.append('')
for subc, (cls, help) in self.subcommands.items():
- lines.append(subc)
- if help:
- lines.append(indent(dedent(help.strip())))
- lines.append('')
- print(os.linesep.join(lines))
-
- def print_help(self, classes=False):
- """Print the help for each Configurable class in self.classes.
-
- If classes=False (the default), only flags and aliases are printed.
- """
- self.print_description()
- self.print_subcommands()
- self.print_options()
-
- if classes:
- help_classes = self.classes
- if help_classes:
- print("Class parameters")
- print("----------------")
- print()
- for p in wrap_paragraphs(self.keyvalue_description):
- print(p)
- print()
-
- for cls in help_classes:
- cls.class_print_help()
- print()
- else:
- print("To see all available configurables, use `--help-all`")
- print()
-
- self.print_examples()
-
- def document_config_options(self):
- """Generate rST format documentation for the config options this application
-
- Returns a multiline string.
- """
- return '\n'.join(c.class_config_rst_doc()
- for c in self._classes_inc_parents())
-
-
- def print_description(self):
- """Print the application description."""
- for p in wrap_paragraphs(self.description):
- print(p)
- print()
-
- def print_examples(self):
- """Print usage and examples.
-
- This usage string goes at the end of the command line help string
- and should contain examples of the application's usage.
- """
- if self.examples:
- print("Examples")
- print("--------")
- print()
- print(indent(dedent(self.examples.strip())))
- print()
-
- def print_version(self):
- """Print the version string."""
- print(self.version)
-
- @catch_config_error
- def initialize_subcommand(self, subc, argv=None):
- """Initialize a subcommand with argv."""
- subapp,help = self.subcommands.get(subc)
-
+ lines.append(subc)
+ if help:
+ lines.append(indent(dedent(help.strip())))
+ lines.append('')
+ print(os.linesep.join(lines))
+
+ def print_help(self, classes=False):
+ """Print the help for each Configurable class in self.classes.
+
+ If classes=False (the default), only flags and aliases are printed.
+ """
+ self.print_description()
+ self.print_subcommands()
+ self.print_options()
+
+ if classes:
+ help_classes = self.classes
+ if help_classes:
+ print("Class parameters")
+ print("----------------")
+ print()
+ for p in wrap_paragraphs(self.keyvalue_description):
+ print(p)
+ print()
+
+ for cls in help_classes:
+ cls.class_print_help()
+ print()
+ else:
+ print("To see all available configurables, use `--help-all`")
+ print()
+
+ self.print_examples()
+
+ def document_config_options(self):
+ """Generate rST format documentation for the config options this application
+
+ Returns a multiline string.
+ """
+ return '\n'.join(c.class_config_rst_doc()
+ for c in self._classes_inc_parents())
+
+
+ def print_description(self):
+ """Print the application description."""
+ for p in wrap_paragraphs(self.description):
+ print(p)
+ print()
+
+ def print_examples(self):
+ """Print usage and examples.
+
+ This usage string goes at the end of the command line help string
+ and should contain examples of the application's usage.
+ """
+ if self.examples:
+ print("Examples")
+ print("--------")
+ print()
+ print(indent(dedent(self.examples.strip())))
+ print()
+
+ def print_version(self):
+ """Print the version string."""
+ print(self.version)
+
+ @catch_config_error
+ def initialize_subcommand(self, subc, argv=None):
+ """Initialize a subcommand with argv."""
+ subapp,help = self.subcommands.get(subc)
+
if isinstance(subapp, six.string_types):
- subapp = import_item(subapp)
-
- # clear existing instances
- self.__class__.clear_instance()
- # instantiate
+ subapp = import_item(subapp)
+
+ # clear existing instances
+ self.__class__.clear_instance()
+ # instantiate
self.subapp = subapp.instance(parent=self)
- # and initialize subapp
- self.subapp.initialize(argv)
-
- def flatten_flags(self):
- """flatten flags and aliases, so cl-args override as expected.
-
- This prevents issues such as an alias pointing to InteractiveShell,
- but a config file setting the same trait in TerminalInteraciveShell
- getting inappropriate priority over the command-line arg.
-
- Only aliases with exactly one descendent in the class list
- will be promoted.
-
- """
- # build a tree of classes in our list that inherit from a particular
- # it will be a dict by parent classname of classes in our list
- # that are descendents
- mro_tree = defaultdict(list)
- for cls in self.classes:
- clsname = cls.__name__
- for parent in cls.mro()[1:-3]:
- # exclude cls itself and Configurable,HasTraits,object
- mro_tree[parent.__name__].append(clsname)
- # flatten aliases, which have the form:
- # { 'alias' : 'Class.trait' }
- aliases = {}
+ # and initialize subapp
+ self.subapp.initialize(argv)
+
+ def flatten_flags(self):
+ """flatten flags and aliases, so cl-args override as expected.
+
+ This prevents issues such as an alias pointing to InteractiveShell,
+ but a config file setting the same trait in TerminalInteraciveShell
+ getting inappropriate priority over the command-line arg.
+
+ Only aliases with exactly one descendent in the class list
+ will be promoted.
+
+ """
+ # build a tree of classes in our list that inherit from a particular
+ # it will be a dict by parent classname of classes in our list
+ # that are descendents
+ mro_tree = defaultdict(list)
+ for cls in self.classes:
+ clsname = cls.__name__
+ for parent in cls.mro()[1:-3]:
+ # exclude cls itself and Configurable,HasTraits,object
+ mro_tree[parent.__name__].append(clsname)
+ # flatten aliases, which have the form:
+ # { 'alias' : 'Class.trait' }
+ aliases = {}
for alias, cls_trait in self.aliases.items():
- cls,trait = cls_trait.split('.',1)
- children = mro_tree[cls]
- if len(children) == 1:
- # exactly one descendent, promote alias
- cls = children[0]
- aliases[alias] = '.'.join([cls,trait])
-
- # flatten flags, which are of the form:
- # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
- flags = {}
+ cls,trait = cls_trait.split('.',1)
+ children = mro_tree[cls]
+ if len(children) == 1:
+ # exactly one descendent, promote alias
+ cls = children[0]
+ aliases[alias] = '.'.join([cls,trait])
+
+ # flatten flags, which are of the form:
+ # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
+ flags = {}
for key, (flagdict, help) in self.flags.items():
- newflag = {}
+ newflag = {}
for cls, subdict in flagdict.items():
- children = mro_tree[cls]
- # exactly one descendent, promote flag section
- if len(children) == 1:
- cls = children[0]
- newflag[cls] = subdict
- flags[key] = (newflag, help)
- return flags, aliases
-
- @catch_config_error
- def parse_command_line(self, argv=None):
- """Parse the command line arguments."""
- argv = sys.argv[1:] if argv is None else argv
- self.argv = [ py3compat.cast_unicode(arg) for arg in argv ]
-
- if argv and argv[0] == 'help':
- # turn `ipython help notebook` into `ipython notebook -h`
- argv = argv[1:] + ['-h']
-
- if self.subcommands and len(argv) > 0:
- # we have subcommands, and one may have been specified
- subc, subargv = argv[0], argv[1:]
- if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
- # it's a subcommand, and *not* a flag or class parameter
- return self.initialize_subcommand(subc, subargv)
-
- # Arguments after a '--' argument are for the script IPython may be
- # about to run, not IPython iteslf. For arguments parsed here (help and
- # version), we want to only search the arguments up to the first
- # occurrence of '--', which we're calling interpreted_argv.
- try:
- interpreted_argv = argv[:argv.index('--')]
- except ValueError:
- interpreted_argv = argv
-
- if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
- self.print_help('--help-all' in interpreted_argv)
- self.exit(0)
-
- if '--version' in interpreted_argv or '-V' in interpreted_argv:
- self.print_version()
- self.exit(0)
-
- # flatten flags&aliases, so cl-args get appropriate priority:
- flags,aliases = self.flatten_flags()
- loader = KVArgParseConfigLoader(argv=argv, aliases=aliases,
- flags=flags, log=self.log)
+ children = mro_tree[cls]
+ # exactly one descendent, promote flag section
+ if len(children) == 1:
+ cls = children[0]
+ newflag[cls] = subdict
+ flags[key] = (newflag, help)
+ return flags, aliases
+
+ @catch_config_error
+ def parse_command_line(self, argv=None):
+ """Parse the command line arguments."""
+ argv = sys.argv[1:] if argv is None else argv
+ self.argv = [ py3compat.cast_unicode(arg) for arg in argv ]
+
+ if argv and argv[0] == 'help':
+ # turn `ipython help notebook` into `ipython notebook -h`
+ argv = argv[1:] + ['-h']
+
+ if self.subcommands and len(argv) > 0:
+ # we have subcommands, and one may have been specified
+ subc, subargv = argv[0], argv[1:]
+ if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
+ # it's a subcommand, and *not* a flag or class parameter
+ return self.initialize_subcommand(subc, subargv)
+
+ # Arguments after a '--' argument are for the script IPython may be
+ # about to run, not IPython iteslf. For arguments parsed here (help and
+ # version), we want to only search the arguments up to the first
+ # occurrence of '--', which we're calling interpreted_argv.
+ try:
+ interpreted_argv = argv[:argv.index('--')]
+ except ValueError:
+ interpreted_argv = argv
+
+ if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
+ self.print_help('--help-all' in interpreted_argv)
+ self.exit(0)
+
+ if '--version' in interpreted_argv or '-V' in interpreted_argv:
+ self.print_version()
+ self.exit(0)
+
+ # flatten flags&aliases, so cl-args get appropriate priority:
+ flags,aliases = self.flatten_flags()
+ loader = KVArgParseConfigLoader(argv=argv, aliases=aliases,
+ flags=flags, log=self.log)
self.cli_config = deepcopy(loader.load_config())
self.update_config(self.cli_config)
- # store unparsed args in extra_args
- self.extra_args = loader.extra_args
-
- @classmethod
+ # store unparsed args in extra_args
+ self.extra_args = loader.extra_args
+
+ @classmethod
def _load_config_files(cls, basefilename, path=None, log=None, raise_config_file_errors=False):
- """Load config files (py,json) by filename and path.
-
- yield each config object in turn.
- """
-
- if not isinstance(path, list):
- path = [path]
- for path in path[::-1]:
- # path list is in descending priority order, so load files backwards:
- pyloader = cls.python_config_loader_class(basefilename+'.py', path=path, log=log)
- if log:
+ """Load config files (py,json) by filename and path.
+
+ yield each config object in turn.
+ """
+
+ if not isinstance(path, list):
+ path = [path]
+ for path in path[::-1]:
+ # path list is in descending priority order, so load files backwards:
+ pyloader = cls.python_config_loader_class(basefilename+'.py', path=path, log=log)
+ if log:
log.debug("Looking for %s in %s", basefilename, path or os.getcwd())
- jsonloader = cls.json_config_loader_class(basefilename+'.json', path=path, log=log)
+ jsonloader = cls.json_config_loader_class(basefilename+'.json', path=path, log=log)
loaded = []
filenames = []
- for loader in [pyloader, jsonloader]:
+ for loader in [pyloader, jsonloader]:
config = None
- try:
- config = loader.load_config()
- except ConfigFileNotFound:
- pass
- except Exception:
- # try to get the full filename, but it will be empty in the
- # unlikely event that the error raised before filefind finished
- filename = loader.full_filename or basefilename
- # problem while running the file
+ try:
+ config = loader.load_config()
+ except ConfigFileNotFound:
+ pass
+ except Exception:
+ # try to get the full filename, but it will be empty in the
+ # unlikely event that the error raised before filefind finished
+ filename = loader.full_filename or basefilename
+ # problem while running the file
if raise_config_file_errors:
raise
- if log:
- log.error("Exception while loading config file %s",
- filename, exc_info=True)
- else:
- if log:
- log.debug("Loaded config file: %s", loader.full_filename)
- if config:
+ if log:
+ log.error("Exception while loading config file %s",
+ filename, exc_info=True)
+ else:
+ if log:
+ log.debug("Loaded config file: %s", loader.full_filename)
+ if config:
for filename, earlier_config in zip(filenames, loaded):
collisions = earlier_config.collisions(config)
if collisions and log:
@@ -587,16 +587,16 @@ class Application(SingletonConfigurable):
yield (config, loader.full_filename)
loaded.append(config)
filenames.append(loader.full_filename)
-
+
@property
def loaded_config_files(self):
"""Currently loaded configuration files"""
return self._loaded_config_files[:]
-
- @catch_config_error
- def load_config_file(self, filename, path=None):
- """Load config files by filename and path."""
- filename, ext = os.path.splitext(filename)
+
+ @catch_config_error
+ def load_config_file(self, filename, path=None):
+ """Load config files by filename and path."""
+ filename, ext = os.path.splitext(filename)
new_config = Config()
for (config, filename) in self._load_config_files(filename, path=path, log=self.log,
raise_config_file_errors=self.raise_config_file_errors,
@@ -607,8 +607,8 @@ class Application(SingletonConfigurable):
# add self.cli_config to preserve CLI config priority
new_config.merge(self.cli_config)
self.update_config(new_config)
-
-
+
+
def _classes_in_config_sample(self):
"""
Yields only classes with own traits, and their subclasses.
@@ -641,71 +641,71 @@ class Application(SingletonConfigurable):
if inc_yes:
yield cl
- def generate_config_file(self):
- """generate default config file from Configurables"""
- lines = ["# Configuration file for %s." % self.name]
- lines.append('')
+ def generate_config_file(self):
+ """generate default config file from Configurables"""
+ lines = ["# Configuration file for %s." % self.name]
+ lines.append('')
for cls in self._classes_in_config_sample():
- lines.append(cls.class_config_section())
- return '\n'.join(lines)
-
- def exit(self, exit_status=0):
- self.log.debug("Exiting application: %s" % self.name)
- sys.exit(exit_status)
-
- @classmethod
- def launch_instance(cls, argv=None, **kwargs):
- """Launch a global instance of this Application
-
- If a global instance already exists, this reinitializes and starts it
- """
- app = cls.instance(**kwargs)
- app.initialize(argv)
- app.start()
-
-#-----------------------------------------------------------------------------
-# utility functions, for convenience
-#-----------------------------------------------------------------------------
-
-def boolean_flag(name, configurable, set_help='', unset_help=''):
- """Helper for building basic --trait, --no-trait flags.
-
- Parameters
- ----------
-
- name : str
- The name of the flag.
- configurable : str
- The 'Class.trait' string of the trait to be set/unset with the flag
- set_help : unicode
- help string for --name flag
- unset_help : unicode
- help string for --no-name flag
-
- Returns
- -------
-
- cfg : dict
- A dict with two keys: 'name', and 'no-name', for setting and unsetting
- the trait, respectively.
- """
- # default helpstrings
- set_help = set_help or "set %s=True"%configurable
- unset_help = unset_help or "set %s=False"%configurable
-
- cls,trait = configurable.split('.')
-
- setter = {cls : {trait : True}}
- unsetter = {cls : {trait : False}}
- return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
-
-
-def get_config():
- """Get the config object for the global Application instance, if there is one
-
- otherwise return an empty config object
- """
- if Application.initialized():
- return Application.instance().config
- else:
- return Config()
+ lines.append(cls.class_config_section())
+ return '\n'.join(lines)
+
+ def exit(self, exit_status=0):
+ self.log.debug("Exiting application: %s" % self.name)
+ sys.exit(exit_status)
+
+ @classmethod
+ def launch_instance(cls, argv=None, **kwargs):
+ """Launch a global instance of this Application
+
+ If a global instance already exists, this reinitializes and starts it
+ """
+ app = cls.instance(**kwargs)
+ app.initialize(argv)
+ app.start()
+
+#-----------------------------------------------------------------------------
+# utility functions, for convenience
+#-----------------------------------------------------------------------------
+
+def boolean_flag(name, configurable, set_help='', unset_help=''):
+ """Helper for building basic --trait, --no-trait flags.
+
+ Parameters
+ ----------
+
+ name : str
+ The name of the flag.
+ configurable : str
+ The 'Class.trait' string of the trait to be set/unset with the flag
+ set_help : unicode
+ help string for --name flag
+ unset_help : unicode
+ help string for --no-name flag
+
+ Returns
+ -------
+
+ cfg : dict
+ A dict with two keys: 'name', and 'no-name', for setting and unsetting
+ the trait, respectively.
+ """
+ # default helpstrings
+ set_help = set_help or "set %s=True"%configurable
+ unset_help = unset_help or "set %s=False"%configurable
+
+ cls,trait = configurable.split('.')
+
+ setter = {cls : {trait : True}}
+ unsetter = {cls : {trait : False}}
+ return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
+
+
+def get_config():
+ """Get the config object for the global Application instance, if there is one
+
+ otherwise return an empty config object
+ """
+ if Application.initialized():
+ return Application.instance().config
+ else:
+ return Config()
diff --git a/contrib/python/traitlets/py2/traitlets/config/configurable.py b/contrib/python/traitlets/py2/traitlets/config/configurable.py
index 1174fcf017..acb81cb208 100644
--- a/contrib/python/traitlets/py2/traitlets/config/configurable.py
+++ b/contrib/python/traitlets/py2/traitlets/config/configurable.py
@@ -1,191 +1,191 @@
-# encoding: utf-8
-"""A base class for objects that are configurable."""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
+# encoding: utf-8
+"""A base class for objects that are configurable."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
from __future__ import print_function, absolute_import
-
-from copy import deepcopy
+
+from copy import deepcopy
import warnings
-
+
from .loader import Config, LazyConfigValue, _is_section_key
-from traitlets.traitlets import HasTraits, Instance, observe, observe_compat, default
-from ipython_genutils.text import indent, dedent, wrap_paragraphs
-
-
-#-----------------------------------------------------------------------------
-# Helper classes for Configurables
-#-----------------------------------------------------------------------------
-
-
-class ConfigurableError(Exception):
- pass
-
-
-class MultipleInstanceError(ConfigurableError):
- pass
-
-#-----------------------------------------------------------------------------
-# Configurable implementation
-#-----------------------------------------------------------------------------
-
-class Configurable(HasTraits):
-
- config = Instance(Config, (), {})
- parent = Instance('traitlets.config.configurable.Configurable', allow_none=True)
-
- def __init__(self, **kwargs):
- """Create a configurable given a config config.
-
- Parameters
- ----------
- config : Config
- If this is empty, default values are used. If config is a
- :class:`Config` instance, it will be used to configure the
- instance.
- parent : Configurable instance, optional
- The parent Configurable instance of this object.
-
- Notes
- -----
- Subclasses of Configurable must call the :meth:`__init__` method of
- :class:`Configurable` *before* doing anything else and using
- :func:`super`::
-
- class MyConfigurable(Configurable):
- def __init__(self, config=None):
- super(MyConfigurable, self).__init__(config=config)
- # Then any other code you need to finish initialization.
-
- This ensures that instances will be configured properly.
- """
- parent = kwargs.pop('parent', None)
- if parent is not None:
- # config is implied from parent
- if kwargs.get('config', None) is None:
- kwargs['config'] = parent.config
- self.parent = parent
-
- config = kwargs.pop('config', None)
-
- # load kwarg traits, other than config
- super(Configurable, self).__init__(**kwargs)
-
- # load config
- if config is not None:
- # We used to deepcopy, but for now we are trying to just save
- # by reference. This *could* have side effects as all components
- # will share config. In fact, I did find such a side effect in
- # _config_changed below. If a config attribute value was a mutable type
- # all instances of a component were getting the same copy, effectively
- # making that a class attribute.
- # self.config = deepcopy(config)
- self.config = config
- else:
- # allow _config_default to return something
- self._load_config(self.config)
-
- # Ensure explicit kwargs are applied after loading config.
- # This is usually redundant, but ensures config doesn't override
- # explicitly assigned values.
- for key, value in kwargs.items():
- setattr(self, key, value)
-
- #-------------------------------------------------------------------------
- # Static trait notifiations
- #-------------------------------------------------------------------------
-
- @classmethod
- def section_names(cls):
- """return section names as a list"""
- return [c.__name__ for c in reversed(cls.__mro__) if
- issubclass(c, Configurable) and issubclass(cls, c)
- ]
-
- def _find_my_config(self, cfg):
- """extract my config from a global Config object
-
- will construct a Config object of only the config values that apply to me
- based on my mro(), as well as those of my parent(s) if they exist.
-
- If I am Bar and my parent is Foo, and their parent is Tim,
- this will return merge following config sections, in this order::
-
- [Bar, Foo.bar, Tim.Foo.Bar]
-
- With the last item being the highest priority.
- """
- cfgs = [cfg]
- if self.parent:
- cfgs.append(self.parent._find_my_config(cfg))
- my_config = Config()
- for c in cfgs:
- for sname in self.section_names():
- # Don't do a blind getattr as that would cause the config to
- # dynamically create the section with name Class.__name__.
- if c._has_section(sname):
- my_config.merge(c[sname])
- return my_config
-
- def _load_config(self, cfg, section_names=None, traits=None):
- """load traits from a Config object"""
-
- if traits is None:
- traits = self.traits(config=True)
- if section_names is None:
- section_names = self.section_names()
-
- my_config = self._find_my_config(cfg)
-
- # hold trait notifications until after all config has been loaded
- with self.hold_trait_notifications():
+from traitlets.traitlets import HasTraits, Instance, observe, observe_compat, default
+from ipython_genutils.text import indent, dedent, wrap_paragraphs
+
+
+#-----------------------------------------------------------------------------
+# Helper classes for Configurables
+#-----------------------------------------------------------------------------
+
+
+class ConfigurableError(Exception):
+ pass
+
+
+class MultipleInstanceError(ConfigurableError):
+ pass
+
+#-----------------------------------------------------------------------------
+# Configurable implementation
+#-----------------------------------------------------------------------------
+
+class Configurable(HasTraits):
+
+ config = Instance(Config, (), {})
+ parent = Instance('traitlets.config.configurable.Configurable', allow_none=True)
+
+ def __init__(self, **kwargs):
+ """Create a configurable given a config config.
+
+ Parameters
+ ----------
+ config : Config
+ If this is empty, default values are used. If config is a
+ :class:`Config` instance, it will be used to configure the
+ instance.
+ parent : Configurable instance, optional
+ The parent Configurable instance of this object.
+
+ Notes
+ -----
+ Subclasses of Configurable must call the :meth:`__init__` method of
+ :class:`Configurable` *before* doing anything else and using
+ :func:`super`::
+
+ class MyConfigurable(Configurable):
+ def __init__(self, config=None):
+ super(MyConfigurable, self).__init__(config=config)
+ # Then any other code you need to finish initialization.
+
+ This ensures that instances will be configured properly.
+ """
+ parent = kwargs.pop('parent', None)
+ if parent is not None:
+ # config is implied from parent
+ if kwargs.get('config', None) is None:
+ kwargs['config'] = parent.config
+ self.parent = parent
+
+ config = kwargs.pop('config', None)
+
+ # load kwarg traits, other than config
+ super(Configurable, self).__init__(**kwargs)
+
+ # load config
+ if config is not None:
+ # We used to deepcopy, but for now we are trying to just save
+ # by reference. This *could* have side effects as all components
+ # will share config. In fact, I did find such a side effect in
+ # _config_changed below. If a config attribute value was a mutable type
+ # all instances of a component were getting the same copy, effectively
+ # making that a class attribute.
+ # self.config = deepcopy(config)
+ self.config = config
+ else:
+ # allow _config_default to return something
+ self._load_config(self.config)
+
+ # Ensure explicit kwargs are applied after loading config.
+ # This is usually redundant, but ensures config doesn't override
+ # explicitly assigned values.
+ for key, value in kwargs.items():
+ setattr(self, key, value)
+
+ #-------------------------------------------------------------------------
+ # Static trait notifiations
+ #-------------------------------------------------------------------------
+
+ @classmethod
+ def section_names(cls):
+ """return section names as a list"""
+ return [c.__name__ for c in reversed(cls.__mro__) if
+ issubclass(c, Configurable) and issubclass(cls, c)
+ ]
+
+ def _find_my_config(self, cfg):
+ """extract my config from a global Config object
+
+ will construct a Config object of only the config values that apply to me
+ based on my mro(), as well as those of my parent(s) if they exist.
+
+ If I am Bar and my parent is Foo, and their parent is Tim,
+ this will return merge following config sections, in this order::
+
+ [Bar, Foo.bar, Tim.Foo.Bar]
+
+ With the last item being the highest priority.
+ """
+ cfgs = [cfg]
+ if self.parent:
+ cfgs.append(self.parent._find_my_config(cfg))
+ my_config = Config()
+ for c in cfgs:
+ for sname in self.section_names():
+ # Don't do a blind getattr as that would cause the config to
+ # dynamically create the section with name Class.__name__.
+ if c._has_section(sname):
+ my_config.merge(c[sname])
+ return my_config
+
+ def _load_config(self, cfg, section_names=None, traits=None):
+ """load traits from a Config object"""
+
+ if traits is None:
+ traits = self.traits(config=True)
+ if section_names is None:
+ section_names = self.section_names()
+
+ my_config = self._find_my_config(cfg)
+
+ # hold trait notifications until after all config has been loaded
+ with self.hold_trait_notifications():
for name, config_value in my_config.items():
- if name in traits:
- if isinstance(config_value, LazyConfigValue):
- # ConfigValue is a wrapper for using append / update on containers
- # without having to copy the initial value
- initial = getattr(self, name)
- config_value = config_value.get_value(initial)
- # We have to do a deepcopy here if we don't deepcopy the entire
- # config object. If we don't, a mutable config_value will be
- # shared by all instances, effectively making it a class attribute.
- setattr(self, name, deepcopy(config_value))
+ if name in traits:
+ if isinstance(config_value, LazyConfigValue):
+ # ConfigValue is a wrapper for using append / update on containers
+ # without having to copy the initial value
+ initial = getattr(self, name)
+ config_value = config_value.get_value(initial)
+ # We have to do a deepcopy here if we don't deepcopy the entire
+ # config object. If we don't, a mutable config_value will be
+ # shared by all instances, effectively making it a class attribute.
+ setattr(self, name, deepcopy(config_value))
elif not _is_section_key(name) and not isinstance(config_value, Config):
- from difflib import get_close_matches
+ from difflib import get_close_matches
if isinstance(self, LoggingConfigurable):
warn = self.log.warning
else:
warn = lambda msg: warnings.warn(msg, stacklevel=9)
- matches = get_close_matches(name, traits)
+ matches = get_close_matches(name, traits)
msg = u"Config option `{option}` not recognized by `{klass}`.".format(
option=name, klass=self.__class__.__name__)
- if len(matches) == 1:
+ if len(matches) == 1:
msg += u" Did you mean `{matches}`?".format(matches=matches[0])
- elif len(matches) >= 1:
+ elif len(matches) >= 1:
msg +=" Did you mean one of: `{matches}`?".format(matches=', '.join(sorted(matches)))
warn(msg)
-
- @observe('config')
- @observe_compat
- def _config_changed(self, change):
- """Update all the class traits having ``config=True`` in metadata.
-
- For any class trait with a ``config`` metadata attribute that is
- ``True``, we update the trait with the value of the corresponding
- config entry.
- """
- # Get all traits with a config metadata entry that is True
- traits = self.traits(config=True)
-
- # We auto-load config section for this class as well as any parent
- # classes that are Configurable subclasses. This starts with Configurable
- # and works down the mro loading the config for each section.
- section_names = self.section_names()
+
+ @observe('config')
+ @observe_compat
+ def _config_changed(self, change):
+ """Update all the class traits having ``config=True`` in metadata.
+
+ For any class trait with a ``config`` metadata attribute that is
+ ``True``, we update the trait with the value of the corresponding
+ config entry.
+ """
+ # Get all traits with a config metadata entry that is True
+ traits = self.traits(config=True)
+
+ # We auto-load config section for this class as well as any parent
+ # classes that are Configurable subclasses. This starts with Configurable
+ # and works down the mro loading the config for each section.
+ section_names = self.section_names()
self._load_config(change.new, traits=traits, section_names=section_names)
-
- def update_config(self, config):
+
+ def update_config(self, config):
"""Update config and load the new values"""
# traitlets prior to 4.2 created a copy of self.config in order to trigger change events.
# Some projects (IPython < 5) relied upon one side effect of this,
@@ -197,236 +197,236 @@ class Configurable(HasTraits):
# load config
self._load_config(config)
# merge it into self.config
- self.config.merge(config)
+ self.config.merge(config)
# TODO: trigger change event if/when dict-update change events take place
# DO NOT trigger full trait-change
-
- @classmethod
- def class_get_help(cls, inst=None):
- """Get the help string for this class in ReST format.
-
- If `inst` is given, it's current trait values will be used in place of
- class defaults.
- """
- assert inst is None or isinstance(inst, cls)
- final_help = []
- final_help.append(u'%s options' % cls.__name__)
- final_help.append(len(final_help[0])*u'-')
- for k, v in sorted(cls.class_traits(config=True).items()):
- help = cls.class_get_trait_help(v, inst)
- final_help.append(help)
- return '\n'.join(final_help)
-
- @classmethod
- def class_get_trait_help(cls, trait, inst=None):
- """Get the help string for a single trait.
-
- If `inst` is given, it's current trait values will be used in place of
- the class default.
- """
- assert inst is None or isinstance(inst, cls)
- lines = []
- header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__)
- lines.append(header)
- if inst is not None:
- lines.append(indent('Current: %r' % getattr(inst, trait.name), 4))
- else:
- try:
- dvr = trait.default_value_repr()
- except Exception:
- dvr = None # ignore defaults we can't construct
- if dvr is not None:
- if len(dvr) > 64:
- dvr = dvr[:61]+'...'
- lines.append(indent('Default: %s' % dvr, 4))
- if 'Enum' in trait.__class__.__name__:
- # include Enum choices
- lines.append(indent('Choices: %r' % (trait.values,)))
-
- help = trait.help
- if help != '':
- help = '\n'.join(wrap_paragraphs(help, 76))
- lines.append(indent(help, 4))
- return '\n'.join(lines)
-
- @classmethod
- def class_print_help(cls, inst=None):
- """Get the help string for a single trait and print it."""
- print(cls.class_get_help(inst))
-
- @classmethod
- def class_config_section(cls):
- """Get the config class config section"""
- def c(s):
- """return a commented, wrapped block."""
- s = '\n\n'.join(wrap_paragraphs(s, 78))
-
+
+ @classmethod
+ def class_get_help(cls, inst=None):
+ """Get the help string for this class in ReST format.
+
+ If `inst` is given, it's current trait values will be used in place of
+ class defaults.
+ """
+ assert inst is None or isinstance(inst, cls)
+ final_help = []
+ final_help.append(u'%s options' % cls.__name__)
+ final_help.append(len(final_help[0])*u'-')
+ for k, v in sorted(cls.class_traits(config=True).items()):
+ help = cls.class_get_trait_help(v, inst)
+ final_help.append(help)
+ return '\n'.join(final_help)
+
+ @classmethod
+ def class_get_trait_help(cls, trait, inst=None):
+ """Get the help string for a single trait.
+
+ If `inst` is given, it's current trait values will be used in place of
+ the class default.
+ """
+ assert inst is None or isinstance(inst, cls)
+ lines = []
+ header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__)
+ lines.append(header)
+ if inst is not None:
+ lines.append(indent('Current: %r' % getattr(inst, trait.name), 4))
+ else:
+ try:
+ dvr = trait.default_value_repr()
+ except Exception:
+ dvr = None # ignore defaults we can't construct
+ if dvr is not None:
+ if len(dvr) > 64:
+ dvr = dvr[:61]+'...'
+ lines.append(indent('Default: %s' % dvr, 4))
+ if 'Enum' in trait.__class__.__name__:
+ # include Enum choices
+ lines.append(indent('Choices: %r' % (trait.values,)))
+
+ help = trait.help
+ if help != '':
+ help = '\n'.join(wrap_paragraphs(help, 76))
+ lines.append(indent(help, 4))
+ return '\n'.join(lines)
+
+ @classmethod
+ def class_print_help(cls, inst=None):
+ """Get the help string for a single trait and print it."""
+ print(cls.class_get_help(inst))
+
+ @classmethod
+ def class_config_section(cls):
+ """Get the config class config section"""
+ def c(s):
+ """return a commented, wrapped block."""
+ s = '\n\n'.join(wrap_paragraphs(s, 78))
+
return '## ' + s.replace('\n', '\n# ')
-
- # section header
- breaker = '#' + '-'*78
+
+ # section header
+ breaker = '#' + '-'*78
parent_classes = ','.join(p.__name__ for p in cls.__bases__)
s = "# %s(%s) configuration" % (cls.__name__, parent_classes)
- lines = [breaker, s, breaker, '']
- # get the description trait
- desc = cls.class_traits().get('description')
- if desc:
- desc = desc.default_value
+ lines = [breaker, s, breaker, '']
+ # get the description trait
+ desc = cls.class_traits().get('description')
+ if desc:
+ desc = desc.default_value
if not desc:
# no description from trait, use __doc__
- desc = getattr(cls, '__doc__', '')
- if desc:
- lines.append(c(desc))
- lines.append('')
-
- for name, trait in sorted(cls.class_own_traits(config=True).items()):
- lines.append(c(trait.help))
+ desc = getattr(cls, '__doc__', '')
+ if desc:
+ lines.append(c(desc))
+ lines.append('')
+
+ for name, trait in sorted(cls.class_own_traits(config=True).items()):
+ lines.append(c(trait.help))
lines.append('#c.%s.%s = %s' % (cls.__name__, name, trait.default_value_repr()))
- lines.append('')
- return '\n'.join(lines)
-
- @classmethod
- def class_config_rst_doc(cls):
- """Generate rST documentation for this class' config options.
-
- Excludes traits defined on parent classes.
- """
- lines = []
- classname = cls.__name__
- for k, trait in sorted(cls.class_own_traits(config=True).items()):
- ttype = trait.__class__.__name__
-
- termline = classname + '.' + trait.name
-
- # Choices or type
- if 'Enum' in ttype:
- # include Enum choices
- termline += ' : ' + '|'.join(repr(x) for x in trait.values)
- else:
- termline += ' : ' + ttype
- lines.append(termline)
-
- # Default value
- try:
- dvr = trait.default_value_repr()
- except Exception:
- dvr = None # ignore defaults we can't construct
- if dvr is not None:
- if len(dvr) > 64:
- dvr = dvr[:61]+'...'
- # Double up backslashes, so they get to the rendered docs
- dvr = dvr.replace('\\n', '\\\\n')
- lines.append(' Default: ``%s``' % dvr)
- lines.append('')
-
- help = trait.help or 'No description'
- lines.append(indent(dedent(help), 4))
-
- # Blank line
- lines.append('')
-
- return '\n'.join(lines)
-
-
-
-class LoggingConfigurable(Configurable):
- """A parent class for Configurables that log.
-
- Subclasses have a log trait, and the default behavior
- is to get the logger from the currently running Application.
- """
-
- log = Instance('logging.Logger')
- @default('log')
- def _log_default(self):
- from traitlets import log
- return log.get_logger()
-
-
-class SingletonConfigurable(LoggingConfigurable):
- """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 _walk_mro(cls):
- """Walk the cls.mro() for parent classes that are also singletons
-
- For use in instance()
- """
-
- for subclass in cls.mro():
- if issubclass(cls, subclass) and \
- issubclass(subclass, SingletonConfigurable) and \
- subclass != SingletonConfigurable:
- yield subclass
-
- @classmethod
- def clear_instance(cls):
- """unset _instance for this class and singleton parents.
- """
- if not cls.initialized():
- return
- for subclass in cls._walk_mro():
- if isinstance(subclass._instance, cls):
- # only clear instances that are instances
- # of the calling class
- subclass._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 traitlets.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
- # parent classes' _instance attribute.
- for subclass in cls._walk_mro():
- subclass._instance = inst
-
- 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
-
-
-
+ lines.append('')
+ return '\n'.join(lines)
+
+ @classmethod
+ def class_config_rst_doc(cls):
+ """Generate rST documentation for this class' config options.
+
+ Excludes traits defined on parent classes.
+ """
+ lines = []
+ classname = cls.__name__
+ for k, trait in sorted(cls.class_own_traits(config=True).items()):
+ ttype = trait.__class__.__name__
+
+ termline = classname + '.' + trait.name
+
+ # Choices or type
+ if 'Enum' in ttype:
+ # include Enum choices
+ termline += ' : ' + '|'.join(repr(x) for x in trait.values)
+ else:
+ termline += ' : ' + ttype
+ lines.append(termline)
+
+ # Default value
+ try:
+ dvr = trait.default_value_repr()
+ except Exception:
+ dvr = None # ignore defaults we can't construct
+ if dvr is not None:
+ if len(dvr) > 64:
+ dvr = dvr[:61]+'...'
+ # Double up backslashes, so they get to the rendered docs
+ dvr = dvr.replace('\\n', '\\\\n')
+ lines.append(' Default: ``%s``' % dvr)
+ lines.append('')
+
+ help = trait.help or 'No description'
+ lines.append(indent(dedent(help), 4))
+
+ # Blank line
+ lines.append('')
+
+ return '\n'.join(lines)
+
+
+
+class LoggingConfigurable(Configurable):
+ """A parent class for Configurables that log.
+
+ Subclasses have a log trait, and the default behavior
+ is to get the logger from the currently running Application.
+ """
+
+ log = Instance('logging.Logger')
+ @default('log')
+ def _log_default(self):
+ from traitlets import log
+ return log.get_logger()
+
+
+class SingletonConfigurable(LoggingConfigurable):
+ """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 _walk_mro(cls):
+ """Walk the cls.mro() for parent classes that are also singletons
+
+ For use in instance()
+ """
+
+ for subclass in cls.mro():
+ if issubclass(cls, subclass) and \
+ issubclass(subclass, SingletonConfigurable) and \
+ subclass != SingletonConfigurable:
+ yield subclass
+
+ @classmethod
+ def clear_instance(cls):
+ """unset _instance for this class and singleton parents.
+ """
+ if not cls.initialized():
+ return
+ for subclass in cls._walk_mro():
+ if isinstance(subclass._instance, cls):
+ # only clear instances that are instances
+ # of the calling class
+ subclass._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 traitlets.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
+ # parent classes' _instance attribute.
+ for subclass in cls._walk_mro():
+ subclass._instance = inst
+
+ 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
+
+
+
diff --git a/contrib/python/traitlets/py2/traitlets/config/loader.py b/contrib/python/traitlets/py2/traitlets/config/loader.py
index 803b36276f..883ef695ac 100644
--- a/contrib/python/traitlets/py2/traitlets/config/loader.py
+++ b/contrib/python/traitlets/py2/traitlets/config/loader.py
@@ -1,392 +1,392 @@
-# encoding: utf-8
-"""A simple configuration system."""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-import argparse
-import copy
-import logging
-import os
-import re
-import sys
-import json
-from ast import literal_eval
-
-from ipython_genutils.path import filefind
-from ipython_genutils import py3compat
-from ipython_genutils.encoding import DEFAULT_ENCODING
+# encoding: utf-8
+"""A simple configuration system."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import argparse
+import copy
+import logging
+import os
+import re
+import sys
+import json
+from ast import literal_eval
+
+from ipython_genutils.path import filefind
+from ipython_genutils import py3compat
+from ipython_genutils.encoding import DEFAULT_ENCODING
from six import text_type
-from traitlets.traitlets import HasTraits, List, Any
-
-#-----------------------------------------------------------------------------
-# Exceptions
-#-----------------------------------------------------------------------------
-
-
-class ConfigError(Exception):
- pass
-
-class ConfigLoaderError(ConfigError):
- pass
-
-class ConfigFileNotFound(ConfigError):
- pass
-
-class ArgumentError(ConfigLoaderError):
- pass
-
-#-----------------------------------------------------------------------------
-# Argparse fix
-#-----------------------------------------------------------------------------
-
-# Unfortunately argparse by default prints help messages to stderr instead of
-# stdout. This makes it annoying to capture long help screens at the command
-# line, since one must know how to pipe stderr, which many users don't know how
-# to do. So we override the print_help method with one that defaults to
-# stdout and use our class instead.
-
-class ArgumentParser(argparse.ArgumentParser):
- """Simple argparse subclass that prints help to stdout by default."""
-
- def print_help(self, file=None):
- if file is None:
- file = sys.stdout
- return super(ArgumentParser, self).print_help(file)
-
- print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
-
-#-----------------------------------------------------------------------------
-# Config class for holding config information
-#-----------------------------------------------------------------------------
-
-class LazyConfigValue(HasTraits):
- """Proxy object for exposing methods on configurable containers
-
- Exposes:
-
- - append, extend, insert on lists
- - update on dicts
- - update, add on sets
- """
-
- _value = None
-
- # list methods
- _extend = List()
- _prepend = List()
-
- def append(self, obj):
- self._extend.append(obj)
-
- def extend(self, other):
- self._extend.extend(other)
-
- def prepend(self, other):
- """like list.extend, but for the front"""
- self._prepend[:0] = other
-
- _inserts = List()
- def insert(self, index, other):
- if not isinstance(index, int):
- raise TypeError("An integer is required")
- self._inserts.append((index, other))
-
- # dict methods
- # update is used for both dict and set
- _update = Any()
- def update(self, other):
- if self._update is None:
- if isinstance(other, dict):
- self._update = {}
- else:
- self._update = set()
- self._update.update(other)
-
- # set methods
- def add(self, obj):
- self.update({obj})
-
- def get_value(self, initial):
- """construct the value from the initial one
-
- after applying any insert / extend / update changes
- """
- if self._value is not None:
- return self._value
- value = copy.deepcopy(initial)
- if isinstance(value, list):
- for idx, obj in self._inserts:
- value.insert(idx, obj)
- value[:0] = self._prepend
- value.extend(self._extend)
-
- elif isinstance(value, dict):
- if self._update:
- value.update(self._update)
- elif isinstance(value, set):
- if self._update:
- value.update(self._update)
- self._value = value
- return value
-
- def to_dict(self):
- """return JSONable dict form of my data
-
- Currently update as dict or set, extend, prepend as lists, and inserts as list of tuples.
- """
- d = {}
- if self._update:
- d['update'] = self._update
- if self._extend:
- d['extend'] = self._extend
- if self._prepend:
- d['prepend'] = self._prepend
- elif self._inserts:
- d['inserts'] = self._inserts
- return d
-
-
-def _is_section_key(key):
- """Is a Config key a section name (does it start with a capital)?"""
- if key and key[0].upper()==key[0] and not key.startswith('_'):
- return True
- else:
- return False
-
-
-class Config(dict):
- """An attribute based dict that can do smart merges."""
-
- def __init__(self, *args, **kwds):
- dict.__init__(self, *args, **kwds)
- self._ensure_subconfig()
-
- def _ensure_subconfig(self):
- """ensure that sub-dicts that should be Config objects are
-
- casts dicts that are under section keys to Config objects,
- which is necessary for constructing Config objects from dict literals.
- """
- for key in self:
- obj = self[key]
- if _is_section_key(key) \
- and isinstance(obj, dict) \
- and not isinstance(obj, Config):
- setattr(self, key, Config(obj))
-
- def _merge(self, other):
- """deprecated alias, use Config.merge()"""
- self.merge(other)
-
- def merge(self, other):
- """merge another config object into this one"""
- to_update = {}
+from traitlets.traitlets import HasTraits, List, Any
+
+#-----------------------------------------------------------------------------
+# Exceptions
+#-----------------------------------------------------------------------------
+
+
+class ConfigError(Exception):
+ pass
+
+class ConfigLoaderError(ConfigError):
+ pass
+
+class ConfigFileNotFound(ConfigError):
+ pass
+
+class ArgumentError(ConfigLoaderError):
+ pass
+
+#-----------------------------------------------------------------------------
+# Argparse fix
+#-----------------------------------------------------------------------------
+
+# Unfortunately argparse by default prints help messages to stderr instead of
+# stdout. This makes it annoying to capture long help screens at the command
+# line, since one must know how to pipe stderr, which many users don't know how
+# to do. So we override the print_help method with one that defaults to
+# stdout and use our class instead.
+
+class ArgumentParser(argparse.ArgumentParser):
+ """Simple argparse subclass that prints help to stdout by default."""
+
+ def print_help(self, file=None):
+ if file is None:
+ file = sys.stdout
+ return super(ArgumentParser, self).print_help(file)
+
+ print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
+
+#-----------------------------------------------------------------------------
+# Config class for holding config information
+#-----------------------------------------------------------------------------
+
+class LazyConfigValue(HasTraits):
+ """Proxy object for exposing methods on configurable containers
+
+ Exposes:
+
+ - append, extend, insert on lists
+ - update on dicts
+ - update, add on sets
+ """
+
+ _value = None
+
+ # list methods
+ _extend = List()
+ _prepend = List()
+
+ def append(self, obj):
+ self._extend.append(obj)
+
+ def extend(self, other):
+ self._extend.extend(other)
+
+ def prepend(self, other):
+ """like list.extend, but for the front"""
+ self._prepend[:0] = other
+
+ _inserts = List()
+ def insert(self, index, other):
+ if not isinstance(index, int):
+ raise TypeError("An integer is required")
+ self._inserts.append((index, other))
+
+ # dict methods
+ # update is used for both dict and set
+ _update = Any()
+ def update(self, other):
+ if self._update is None:
+ if isinstance(other, dict):
+ self._update = {}
+ else:
+ self._update = set()
+ self._update.update(other)
+
+ # set methods
+ def add(self, obj):
+ self.update({obj})
+
+ def get_value(self, initial):
+ """construct the value from the initial one
+
+ after applying any insert / extend / update changes
+ """
+ if self._value is not None:
+ return self._value
+ value = copy.deepcopy(initial)
+ if isinstance(value, list):
+ for idx, obj in self._inserts:
+ value.insert(idx, obj)
+ value[:0] = self._prepend
+ value.extend(self._extend)
+
+ elif isinstance(value, dict):
+ if self._update:
+ value.update(self._update)
+ elif isinstance(value, set):
+ if self._update:
+ value.update(self._update)
+ self._value = value
+ return value
+
+ def to_dict(self):
+ """return JSONable dict form of my data
+
+ Currently update as dict or set, extend, prepend as lists, and inserts as list of tuples.
+ """
+ d = {}
+ if self._update:
+ d['update'] = self._update
+ if self._extend:
+ d['extend'] = self._extend
+ if self._prepend:
+ d['prepend'] = self._prepend
+ elif self._inserts:
+ d['inserts'] = self._inserts
+ return d
+
+
+def _is_section_key(key):
+ """Is a Config key a section name (does it start with a capital)?"""
+ if key and key[0].upper()==key[0] and not key.startswith('_'):
+ return True
+ else:
+ return False
+
+
+class Config(dict):
+ """An attribute based dict that can do smart merges."""
+
+ def __init__(self, *args, **kwds):
+ dict.__init__(self, *args, **kwds)
+ self._ensure_subconfig()
+
+ def _ensure_subconfig(self):
+ """ensure that sub-dicts that should be Config objects are
+
+ casts dicts that are under section keys to Config objects,
+ which is necessary for constructing Config objects from dict literals.
+ """
+ for key in self:
+ obj = self[key]
+ if _is_section_key(key) \
+ and isinstance(obj, dict) \
+ and not isinstance(obj, Config):
+ setattr(self, key, Config(obj))
+
+ def _merge(self, other):
+ """deprecated alias, use Config.merge()"""
+ self.merge(other)
+
+ def merge(self, other):
+ """merge another config object into this one"""
+ to_update = {}
for k, v in other.items():
- if k not in self:
- to_update[k] = v
- else: # I have this key
- if isinstance(v, Config) and isinstance(self[k], Config):
- # Recursively merge common sub Configs
- self[k].merge(v)
- else:
- # Plain updates for non-Configs
- to_update[k] = v
-
- self.update(to_update)
-
- def collisions(self, other):
- """Check for collisions between two config objects.
-
- Returns a dict of the form {"Class": {"trait": "collision message"}}`,
- indicating which values have been ignored.
-
- An empty dict indicates no collisions.
- """
- collisions = {}
- for section in self:
- if section not in other:
- continue
- mine = self[section]
- theirs = other[section]
- for key in mine:
- if key in theirs and mine[key] != theirs[key]:
- collisions.setdefault(section, {})
- collisions[section][key] = "%r ignored, using %r" % (mine[key], theirs[key])
- return collisions
-
- def __contains__(self, key):
- # allow nested contains of the form `"Section.key" in config`
- if '.' in key:
- first, remainder = key.split('.', 1)
- if first not in self:
- return False
- return remainder in self[first]
-
- return super(Config, self).__contains__(key)
-
- # .has_key is deprecated for dictionaries.
- has_key = __contains__
-
- def _has_section(self, key):
- return _is_section_key(key) and key in self
-
- def copy(self):
- return type(self)(dict.copy(self))
-
- def __copy__(self):
- return self.copy()
-
- def __deepcopy__(self, memo):
- new_config = type(self)()
- for key, value in self.items():
- if isinstance(value, (Config, LazyConfigValue)):
- # deep copy config objects
- value = copy.deepcopy(value, memo)
- elif type(value) in {dict, list, set, tuple}:
- # shallow copy plain container traits
- value = copy.copy(value)
- new_config[key] = value
- return new_config
-
- def __getitem__(self, key):
- try:
- return dict.__getitem__(self, key)
- except KeyError:
- if _is_section_key(key):
- c = Config()
- dict.__setitem__(self, key, c)
- return c
- elif not key.startswith('_'):
- # undefined, create lazy value, used for container methods
- v = LazyConfigValue()
- dict.__setitem__(self, key, v)
- return v
- else:
- raise KeyError
-
- def __setitem__(self, key, value):
- if _is_section_key(key):
- if not isinstance(value, Config):
- raise ValueError('values whose keys begin with an uppercase '
- 'char must be Config instances: %r, %r' % (key, value))
- dict.__setitem__(self, key, value)
-
- def __getattr__(self, key):
- if key.startswith('__'):
- return dict.__getattr__(self, key)
- try:
- return self.__getitem__(key)
- except KeyError as e:
- raise AttributeError(e)
-
- def __setattr__(self, key, value):
- if key.startswith('__'):
- return dict.__setattr__(self, key, value)
- try:
- self.__setitem__(key, value)
- except KeyError as e:
- raise AttributeError(e)
-
- def __delattr__(self, key):
- if key.startswith('__'):
- return dict.__delattr__(self, key)
- try:
- dict.__delitem__(self, key)
- except KeyError as e:
- raise AttributeError(e)
-
-
-#-----------------------------------------------------------------------------
-# Config loading classes
-#-----------------------------------------------------------------------------
-
-
-class ConfigLoader(object):
- """A object for loading configurations from just about anywhere.
-
- The resulting configuration is packaged as a :class:`Config`.
-
- Notes
- -----
- A :class:`ConfigLoader` does one thing: load a config from a source
- (file, command line arguments) and returns the data as a :class:`Config` object.
- There are lots of things that :class:`ConfigLoader` does not do. It does
- not implement complex logic for finding config files. It does not handle
- default values or merge multiple configs. These things need to be
- handled elsewhere.
- """
-
- def _log_default(self):
- from traitlets.log import get_logger
- return get_logger()
-
- def __init__(self, log=None):
- """A base class for config loaders.
-
- log : instance of :class:`logging.Logger` to use.
- By default loger of :meth:`traitlets.config.application.Application.instance()`
- will be used
-
- Examples
- --------
-
- >>> cl = ConfigLoader()
- >>> config = cl.load_config()
- >>> config
- {}
- """
- self.clear()
- if log is None:
- self.log = self._log_default()
- self.log.debug('Using default logger')
- else:
- self.log = log
-
- def clear(self):
- self.config = Config()
-
- def load_config(self):
- """Load a config from somewhere, return a :class:`Config` instance.
-
- Usually, this will cause self.config to be set and then returned.
- However, in most cases, :meth:`ConfigLoader.clear` should be called
- to erase any previous state.
- """
- self.clear()
- return self.config
-
-
-class FileConfigLoader(ConfigLoader):
- """A base class for file based configurations.
-
- As we add more file based config loaders, the common logic should go
- here.
- """
-
- def __init__(self, filename, path=None, **kw):
- """Build a config loader for a filename and path.
-
- Parameters
- ----------
- filename : str
- The file name of the config file.
- path : str, list, tuple
- The path to search for the config file on, or a sequence of
- paths to try in order.
- """
- super(FileConfigLoader, self).__init__(**kw)
- self.filename = filename
- self.path = path
- self.full_filename = ''
-
- def _find_file(self):
- """Try to find the file by searching the paths."""
- self.full_filename = filefind(self.filename, self.path)
-
-class JSONFileConfigLoader(FileConfigLoader):
+ if k not in self:
+ to_update[k] = v
+ else: # I have this key
+ if isinstance(v, Config) and isinstance(self[k], Config):
+ # Recursively merge common sub Configs
+ self[k].merge(v)
+ else:
+ # Plain updates for non-Configs
+ to_update[k] = v
+
+ self.update(to_update)
+
+ def collisions(self, other):
+ """Check for collisions between two config objects.
+
+ Returns a dict of the form {"Class": {"trait": "collision message"}}`,
+ indicating which values have been ignored.
+
+ An empty dict indicates no collisions.
+ """
+ collisions = {}
+ for section in self:
+ if section not in other:
+ continue
+ mine = self[section]
+ theirs = other[section]
+ for key in mine:
+ if key in theirs and mine[key] != theirs[key]:
+ collisions.setdefault(section, {})
+ collisions[section][key] = "%r ignored, using %r" % (mine[key], theirs[key])
+ return collisions
+
+ def __contains__(self, key):
+ # allow nested contains of the form `"Section.key" in config`
+ if '.' in key:
+ first, remainder = key.split('.', 1)
+ if first not in self:
+ return False
+ return remainder in self[first]
+
+ return super(Config, self).__contains__(key)
+
+ # .has_key is deprecated for dictionaries.
+ has_key = __contains__
+
+ def _has_section(self, key):
+ return _is_section_key(key) and key in self
+
+ def copy(self):
+ return type(self)(dict.copy(self))
+
+ def __copy__(self):
+ return self.copy()
+
+ def __deepcopy__(self, memo):
+ new_config = type(self)()
+ for key, value in self.items():
+ if isinstance(value, (Config, LazyConfigValue)):
+ # deep copy config objects
+ value = copy.deepcopy(value, memo)
+ elif type(value) in {dict, list, set, tuple}:
+ # shallow copy plain container traits
+ value = copy.copy(value)
+ new_config[key] = value
+ return new_config
+
+ def __getitem__(self, key):
+ try:
+ return dict.__getitem__(self, key)
+ except KeyError:
+ if _is_section_key(key):
+ c = Config()
+ dict.__setitem__(self, key, c)
+ return c
+ elif not key.startswith('_'):
+ # undefined, create lazy value, used for container methods
+ v = LazyConfigValue()
+ dict.__setitem__(self, key, v)
+ return v
+ else:
+ raise KeyError
+
+ def __setitem__(self, key, value):
+ if _is_section_key(key):
+ if not isinstance(value, Config):
+ raise ValueError('values whose keys begin with an uppercase '
+ 'char must be Config instances: %r, %r' % (key, value))
+ dict.__setitem__(self, key, value)
+
+ def __getattr__(self, key):
+ if key.startswith('__'):
+ return dict.__getattr__(self, key)
+ try:
+ return self.__getitem__(key)
+ except KeyError as e:
+ raise AttributeError(e)
+
+ def __setattr__(self, key, value):
+ if key.startswith('__'):
+ return dict.__setattr__(self, key, value)
+ try:
+ self.__setitem__(key, value)
+ except KeyError as e:
+ raise AttributeError(e)
+
+ def __delattr__(self, key):
+ if key.startswith('__'):
+ return dict.__delattr__(self, key)
+ try:
+ dict.__delitem__(self, key)
+ except KeyError as e:
+ raise AttributeError(e)
+
+
+#-----------------------------------------------------------------------------
+# Config loading classes
+#-----------------------------------------------------------------------------
+
+
+class ConfigLoader(object):
+ """A object for loading configurations from just about anywhere.
+
+ The resulting configuration is packaged as a :class:`Config`.
+
+ Notes
+ -----
+ A :class:`ConfigLoader` does one thing: load a config from a source
+ (file, command line arguments) and returns the data as a :class:`Config` object.
+ There are lots of things that :class:`ConfigLoader` does not do. It does
+ not implement complex logic for finding config files. It does not handle
+ default values or merge multiple configs. These things need to be
+ handled elsewhere.
+ """
+
+ def _log_default(self):
+ from traitlets.log import get_logger
+ return get_logger()
+
+ def __init__(self, log=None):
+ """A base class for config loaders.
+
+ log : instance of :class:`logging.Logger` to use.
+ By default loger of :meth:`traitlets.config.application.Application.instance()`
+ will be used
+
+ Examples
+ --------
+
+ >>> cl = ConfigLoader()
+ >>> config = cl.load_config()
+ >>> config
+ {}
+ """
+ self.clear()
+ if log is None:
+ self.log = self._log_default()
+ self.log.debug('Using default logger')
+ else:
+ self.log = log
+
+ def clear(self):
+ self.config = Config()
+
+ def load_config(self):
+ """Load a config from somewhere, return a :class:`Config` instance.
+
+ Usually, this will cause self.config to be set and then returned.
+ However, in most cases, :meth:`ConfigLoader.clear` should be called
+ to erase any previous state.
+ """
+ self.clear()
+ return self.config
+
+
+class FileConfigLoader(ConfigLoader):
+ """A base class for file based configurations.
+
+ As we add more file based config loaders, the common logic should go
+ here.
+ """
+
+ def __init__(self, filename, path=None, **kw):
+ """Build a config loader for a filename and path.
+
+ Parameters
+ ----------
+ filename : str
+ The file name of the config file.
+ path : str, list, tuple
+ The path to search for the config file on, or a sequence of
+ paths to try in order.
+ """
+ super(FileConfigLoader, self).__init__(**kw)
+ self.filename = filename
+ self.path = path
+ self.full_filename = ''
+
+ def _find_file(self):
+ """Try to find the file by searching the paths."""
+ self.full_filename = filefind(self.filename, self.path)
+
+class JSONFileConfigLoader(FileConfigLoader):
"""A JSON file loader for config
-
+
Can also act as a context manager that rewrite the configuration file to disk on exit.
Example::
@@ -396,36 +396,36 @@ class JSONFileConfigLoader(FileConfigLoader):
"""
- def load_config(self):
- """Load the config from a file and return it as a Config object."""
- self.clear()
- try:
- self._find_file()
- except IOError as e:
- raise ConfigFileNotFound(str(e))
- dct = self._read_file_as_dict()
- self.config = self._convert_to_config(dct)
- return self.config
-
- def _read_file_as_dict(self):
- with open(self.full_filename) as f:
- return json.load(f)
-
- def _convert_to_config(self, dictionary):
- if 'version' in dictionary:
- version = dictionary.pop('version')
- else:
- version = 1
-
- if version == 1:
- return Config(dictionary)
- else:
- raise ValueError('Unknown version of JSON config file: {version}'.format(version=version))
-
+ def load_config(self):
+ """Load the config from a file and return it as a Config object."""
+ self.clear()
+ try:
+ self._find_file()
+ except IOError as e:
+ raise ConfigFileNotFound(str(e))
+ dct = self._read_file_as_dict()
+ self.config = self._convert_to_config(dct)
+ return self.config
+
+ def _read_file_as_dict(self):
+ with open(self.full_filename) as f:
+ return json.load(f)
+
+ def _convert_to_config(self, dictionary):
+ if 'version' in dictionary:
+ version = dictionary.pop('version')
+ else:
+ version = 1
+
+ if version == 1:
+ return Config(dictionary)
+ else:
+ raise ValueError('Unknown version of JSON config file: {version}'.format(version=version))
+
def __enter__(self):
self.load_config()
return self.config
-
+
def __exit__(self, exc_type, exc_value, traceback):
"""
Exit the context manager but do not handle any errors.
@@ -440,418 +440,418 @@ class JSONFileConfigLoader(FileConfigLoader):
-class PyFileConfigLoader(FileConfigLoader):
- """A config loader for pure python files.
-
- This is responsible for locating a Python config file by filename and
- path, then executing it to construct a Config object.
- """
-
- def load_config(self):
- """Load the config from a file and return it as a Config object."""
- self.clear()
- try:
- self._find_file()
- except IOError as e:
- raise ConfigFileNotFound(str(e))
- self._read_file_as_dict()
- return self.config
-
- def load_subconfig(self, fname, path=None):
- """Injected into config file namespace as load_subconfig"""
- if path is None:
- path = self.path
-
- loader = self.__class__(fname, path)
- try:
- sub_config = loader.load_config()
- except ConfigFileNotFound:
- # Pass silently if the sub config is not there,
- # treat it as an empty config file.
- pass
- else:
- self.config.merge(sub_config)
-
- def _read_file_as_dict(self):
- """Load the config file into self.config, with recursive loading."""
- def get_config():
- """Unnecessary now, but a deprecation warning is more trouble than it's worth."""
- return self.config
-
- namespace = dict(
- c=self.config,
- load_subconfig=self.load_subconfig,
- get_config=get_config,
- __file__=self.full_filename,
- )
- fs_encoding = sys.getfilesystemencoding() or 'ascii'
- conf_filename = self.full_filename.encode(fs_encoding)
- py3compat.execfile(conf_filename, namespace)
-
-
-class CommandLineConfigLoader(ConfigLoader):
- """A config loader for command line arguments.
-
- As we add more command line based loaders, the common logic should go
- here.
- """
-
- def _exec_config_str(self, lhs, rhs):
- """execute self.config.<lhs> = <rhs>
-
- * expands ~ with expanduser
- * tries to assign with literal_eval, otherwise assigns with just the string,
- allowing `--C.a=foobar` and `--C.a="foobar"` to be equivalent. *Not*
- equivalent are `--C.a=4` and `--C.a='4'`.
- """
- rhs = os.path.expanduser(rhs)
- try:
- # Try to see if regular Python syntax will work. This
- # won't handle strings as the quote marks are removed
- # by the system shell.
- value = literal_eval(rhs)
- except (NameError, SyntaxError, ValueError):
- # This case happens if the rhs is a string.
- value = rhs
-
- exec(u'self.config.%s = value' % lhs)
-
- def _load_flag(self, cfg):
- """update self.config from a flag, which can be a dict or Config"""
- if isinstance(cfg, (dict, Config)):
- # don't clobber whole config sections, update
- # each section from config:
+class PyFileConfigLoader(FileConfigLoader):
+ """A config loader for pure python files.
+
+ This is responsible for locating a Python config file by filename and
+ path, then executing it to construct a Config object.
+ """
+
+ def load_config(self):
+ """Load the config from a file and return it as a Config object."""
+ self.clear()
+ try:
+ self._find_file()
+ except IOError as e:
+ raise ConfigFileNotFound(str(e))
+ self._read_file_as_dict()
+ return self.config
+
+ def load_subconfig(self, fname, path=None):
+ """Injected into config file namespace as load_subconfig"""
+ if path is None:
+ path = self.path
+
+ loader = self.__class__(fname, path)
+ try:
+ sub_config = loader.load_config()
+ except ConfigFileNotFound:
+ # Pass silently if the sub config is not there,
+ # treat it as an empty config file.
+ pass
+ else:
+ self.config.merge(sub_config)
+
+ def _read_file_as_dict(self):
+ """Load the config file into self.config, with recursive loading."""
+ def get_config():
+ """Unnecessary now, but a deprecation warning is more trouble than it's worth."""
+ return self.config
+
+ namespace = dict(
+ c=self.config,
+ load_subconfig=self.load_subconfig,
+ get_config=get_config,
+ __file__=self.full_filename,
+ )
+ fs_encoding = sys.getfilesystemencoding() or 'ascii'
+ conf_filename = self.full_filename.encode(fs_encoding)
+ py3compat.execfile(conf_filename, namespace)
+
+
+class CommandLineConfigLoader(ConfigLoader):
+ """A config loader for command line arguments.
+
+ As we add more command line based loaders, the common logic should go
+ here.
+ """
+
+ def _exec_config_str(self, lhs, rhs):
+ """execute self.config.<lhs> = <rhs>
+
+ * expands ~ with expanduser
+ * tries to assign with literal_eval, otherwise assigns with just the string,
+ allowing `--C.a=foobar` and `--C.a="foobar"` to be equivalent. *Not*
+ equivalent are `--C.a=4` and `--C.a='4'`.
+ """
+ rhs = os.path.expanduser(rhs)
+ try:
+ # Try to see if regular Python syntax will work. This
+ # won't handle strings as the quote marks are removed
+ # by the system shell.
+ value = literal_eval(rhs)
+ except (NameError, SyntaxError, ValueError):
+ # This case happens if the rhs is a string.
+ value = rhs
+
+ exec(u'self.config.%s = value' % lhs)
+
+ def _load_flag(self, cfg):
+ """update self.config from a flag, which can be a dict or Config"""
+ if isinstance(cfg, (dict, Config)):
+ # don't clobber whole config sections, update
+ # each section from config:
for sec,c in cfg.items():
- self.config[sec].update(c)
- else:
- raise TypeError("Invalid flag: %r" % cfg)
-
-# raw --identifier=value pattern
-# but *also* accept '-' as wordsep, for aliases
-# accepts: --foo=a
-# --Class.trait=value
-# --alias-name=value
-# rejects: -foo=value
-# --foo
-# --Class.trait
-kv_pattern = re.compile(r'\-\-[A-Za-z][\w\-]*(\.[\w\-]+)*\=.*')
-
-# just flags, no assignments, with two *or one* leading '-'
-# accepts: --foo
-# -foo-bar-again
-# rejects: --anything=anything
-# --two.word
-
-flag_pattern = re.compile(r'\-\-?\w+[\-\w]*$')
-
-class KeyValueConfigLoader(CommandLineConfigLoader):
- """A config loader that loads key value pairs from the command line.
-
- This allows command line options to be gives in the following form::
-
- ipython --profile="foo" --InteractiveShell.autocall=False
- """
-
- def __init__(self, argv=None, aliases=None, flags=None, **kw):
- """Create a key value pair config loader.
-
- Parameters
- ----------
- argv : list
- A list that has the form of sys.argv[1:] which has unicode
- elements of the form u"key=value". If this is None (default),
- then sys.argv[1:] will be used.
- aliases : dict
- A dict of aliases for configurable traits.
- Keys are the short aliases, Values are the resolved trait.
- Of the form: `{'alias' : 'Configurable.trait'}`
- flags : dict
- A dict of flags, keyed by str name. Vaues can be Config objects,
- dicts, or "key=value" strings. If Config or dict, when the flag
- is triggered, The flag is loaded as `self.config.update(m)`.
-
- Returns
- -------
- config : Config
- The resulting Config object.
-
- Examples
- --------
-
- >>> from traitlets.config.loader import KeyValueConfigLoader
- >>> cl = KeyValueConfigLoader()
- >>> d = cl.load_config(["--A.name='brian'","--B.number=0"])
- >>> sorted(d.items())
- [('A', {'name': 'brian'}), ('B', {'number': 0})]
- """
- super(KeyValueConfigLoader, self).__init__(**kw)
- if argv is None:
- argv = sys.argv[1:]
- self.argv = argv
- self.aliases = aliases or {}
- self.flags = flags or {}
-
-
- def clear(self):
- super(KeyValueConfigLoader, self).clear()
- self.extra_args = []
-
-
- def _decode_argv(self, argv, enc=None):
- """decode argv if bytes, using stdin.encoding, falling back on default enc"""
- uargv = []
- if enc is None:
- enc = DEFAULT_ENCODING
- for arg in argv:
+ self.config[sec].update(c)
+ else:
+ raise TypeError("Invalid flag: %r" % cfg)
+
+# raw --identifier=value pattern
+# but *also* accept '-' as wordsep, for aliases
+# accepts: --foo=a
+# --Class.trait=value
+# --alias-name=value
+# rejects: -foo=value
+# --foo
+# --Class.trait
+kv_pattern = re.compile(r'\-\-[A-Za-z][\w\-]*(\.[\w\-]+)*\=.*')
+
+# just flags, no assignments, with two *or one* leading '-'
+# accepts: --foo
+# -foo-bar-again
+# rejects: --anything=anything
+# --two.word
+
+flag_pattern = re.compile(r'\-\-?\w+[\-\w]*$')
+
+class KeyValueConfigLoader(CommandLineConfigLoader):
+ """A config loader that loads key value pairs from the command line.
+
+ This allows command line options to be gives in the following form::
+
+ ipython --profile="foo" --InteractiveShell.autocall=False
+ """
+
+ def __init__(self, argv=None, aliases=None, flags=None, **kw):
+ """Create a key value pair config loader.
+
+ Parameters
+ ----------
+ argv : list
+ A list that has the form of sys.argv[1:] which has unicode
+ elements of the form u"key=value". If this is None (default),
+ then sys.argv[1:] will be used.
+ aliases : dict
+ A dict of aliases for configurable traits.
+ Keys are the short aliases, Values are the resolved trait.
+ Of the form: `{'alias' : 'Configurable.trait'}`
+ flags : dict
+ A dict of flags, keyed by str name. Vaues can be Config objects,
+ dicts, or "key=value" strings. If Config or dict, when the flag
+ is triggered, The flag is loaded as `self.config.update(m)`.
+
+ Returns
+ -------
+ config : Config
+ The resulting Config object.
+
+ Examples
+ --------
+
+ >>> from traitlets.config.loader import KeyValueConfigLoader
+ >>> cl = KeyValueConfigLoader()
+ >>> d = cl.load_config(["--A.name='brian'","--B.number=0"])
+ >>> sorted(d.items())
+ [('A', {'name': 'brian'}), ('B', {'number': 0})]
+ """
+ super(KeyValueConfigLoader, self).__init__(**kw)
+ if argv is None:
+ argv = sys.argv[1:]
+ self.argv = argv
+ self.aliases = aliases or {}
+ self.flags = flags or {}
+
+
+ def clear(self):
+ super(KeyValueConfigLoader, self).clear()
+ self.extra_args = []
+
+
+ def _decode_argv(self, argv, enc=None):
+ """decode argv if bytes, using stdin.encoding, falling back on default enc"""
+ uargv = []
+ if enc is None:
+ enc = DEFAULT_ENCODING
+ for arg in argv:
if not isinstance(arg, text_type):
- # only decode if not already decoded
- arg = arg.decode(enc)
- uargv.append(arg)
- return uargv
-
-
- def load_config(self, argv=None, aliases=None, flags=None):
- """Parse the configuration and generate the Config object.
-
- After loading, any arguments that are not key-value or
- flags will be stored in self.extra_args - a list of
- unparsed command-line arguments. This is used for
- arguments such as input files or subcommands.
-
- Parameters
- ----------
- argv : list, optional
- A list that has the form of sys.argv[1:] which has unicode
- elements of the form u"key=value". If this is None (default),
- then self.argv will be used.
- aliases : dict
- A dict of aliases for configurable traits.
- Keys are the short aliases, Values are the resolved trait.
- Of the form: `{'alias' : 'Configurable.trait'}`
- flags : dict
- A dict of flags, keyed by str name. Values can be Config objects
- or dicts. When the flag is triggered, The config is loaded as
- `self.config.update(cfg)`.
- """
- self.clear()
- if argv is None:
- argv = self.argv
- if aliases is None:
- aliases = self.aliases
- if flags is None:
- flags = self.flags
-
- # ensure argv is a list of unicode strings:
- uargv = self._decode_argv(argv)
- for idx,raw in enumerate(uargv):
- # strip leading '-'
- item = raw.lstrip('-')
-
- if raw == '--':
- # don't parse arguments after '--'
- # this is useful for relaying arguments to scripts, e.g.
- # ipython -i foo.py --matplotlib=qt -- args after '--' go-to-foo.py
- self.extra_args.extend(uargv[idx+1:])
- break
-
- if kv_pattern.match(raw):
- lhs,rhs = item.split('=',1)
- # Substitute longnames for aliases.
- if lhs in aliases:
- lhs = aliases[lhs]
- if '.' not in lhs:
- # probably a mistyped alias, but not technically illegal
- self.log.warning("Unrecognized alias: '%s', it will probably have no effect.", raw)
- try:
- self._exec_config_str(lhs, rhs)
- except Exception:
- raise ArgumentError("Invalid argument: '%s'" % raw)
-
- elif flag_pattern.match(raw):
- if item in flags:
- cfg,help = flags[item]
- self._load_flag(cfg)
- else:
- raise ArgumentError("Unrecognized flag: '%s'"%raw)
- elif raw.startswith('-'):
- kv = '--'+item
- if kv_pattern.match(kv):
- raise ArgumentError("Invalid argument: '%s', did you mean '%s'?"%(raw, kv))
- else:
- raise ArgumentError("Invalid argument: '%s'"%raw)
- else:
- # keep all args that aren't valid in a list,
- # in case our parent knows what to do with them.
- self.extra_args.append(item)
- return self.config
-
-class ArgParseConfigLoader(CommandLineConfigLoader):
- """A loader that uses the argparse module to load from the command line."""
-
- def __init__(self, argv=None, aliases=None, flags=None, log=None, *parser_args, **parser_kw):
- """Create a config loader for use with argparse.
-
- Parameters
- ----------
-
- argv : optional, list
- If given, used to read command-line arguments from, otherwise
- sys.argv[1:] is used.
-
- parser_args : tuple
- A tuple of positional arguments that will be passed to the
- constructor of :class:`argparse.ArgumentParser`.
-
- parser_kw : dict
- A tuple of keyword arguments that will be passed to the
- constructor of :class:`argparse.ArgumentParser`.
-
- Returns
- -------
- config : Config
- The resulting Config object.
- """
- super(CommandLineConfigLoader, self).__init__(log=log)
- self.clear()
- if argv is None:
- argv = sys.argv[1:]
- self.argv = argv
- self.aliases = aliases or {}
- self.flags = flags or {}
-
- self.parser_args = parser_args
- self.version = parser_kw.pop("version", None)
- kwargs = dict(argument_default=argparse.SUPPRESS)
- kwargs.update(parser_kw)
- self.parser_kw = kwargs
-
- def load_config(self, argv=None, aliases=None, flags=None):
- """Parse command line arguments and return as a Config object.
-
- Parameters
- ----------
-
- args : optional, list
- If given, a list with the structure of sys.argv[1:] to parse
- arguments from. If not given, the instance's self.argv attribute
- (given at construction time) is used."""
- self.clear()
- if argv is None:
- argv = self.argv
- if aliases is None:
- aliases = self.aliases
- if flags is None:
- flags = self.flags
- self._create_parser(aliases, flags)
- self._parse_args(argv)
- self._convert_to_config()
- return self.config
-
- def get_extra_args(self):
- if hasattr(self, 'extra_args'):
- return self.extra_args
- else:
- return []
-
- def _create_parser(self, aliases=None, flags=None):
- self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
- self._add_arguments(aliases, flags)
-
- def _add_arguments(self, aliases=None, flags=None):
- raise NotImplementedError("subclasses must implement _add_arguments")
-
- def _parse_args(self, args):
- """self.parser->self.parsed_data"""
- # decode sys.argv to support unicode command-line options
- enc = DEFAULT_ENCODING
- uargs = [py3compat.cast_unicode(a, enc) for a in args]
- self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
-
- def _convert_to_config(self):
- """self.parsed_data->self.config"""
+ # only decode if not already decoded
+ arg = arg.decode(enc)
+ uargv.append(arg)
+ return uargv
+
+
+ def load_config(self, argv=None, aliases=None, flags=None):
+ """Parse the configuration and generate the Config object.
+
+ After loading, any arguments that are not key-value or
+ flags will be stored in self.extra_args - a list of
+ unparsed command-line arguments. This is used for
+ arguments such as input files or subcommands.
+
+ Parameters
+ ----------
+ argv : list, optional
+ A list that has the form of sys.argv[1:] which has unicode
+ elements of the form u"key=value". If this is None (default),
+ then self.argv will be used.
+ aliases : dict
+ A dict of aliases for configurable traits.
+ Keys are the short aliases, Values are the resolved trait.
+ Of the form: `{'alias' : 'Configurable.trait'}`
+ flags : dict
+ A dict of flags, keyed by str name. Values can be Config objects
+ or dicts. When the flag is triggered, The config is loaded as
+ `self.config.update(cfg)`.
+ """
+ self.clear()
+ if argv is None:
+ argv = self.argv
+ if aliases is None:
+ aliases = self.aliases
+ if flags is None:
+ flags = self.flags
+
+ # ensure argv is a list of unicode strings:
+ uargv = self._decode_argv(argv)
+ for idx,raw in enumerate(uargv):
+ # strip leading '-'
+ item = raw.lstrip('-')
+
+ if raw == '--':
+ # don't parse arguments after '--'
+ # this is useful for relaying arguments to scripts, e.g.
+ # ipython -i foo.py --matplotlib=qt -- args after '--' go-to-foo.py
+ self.extra_args.extend(uargv[idx+1:])
+ break
+
+ if kv_pattern.match(raw):
+ lhs,rhs = item.split('=',1)
+ # Substitute longnames for aliases.
+ if lhs in aliases:
+ lhs = aliases[lhs]
+ if '.' not in lhs:
+ # probably a mistyped alias, but not technically illegal
+ self.log.warning("Unrecognized alias: '%s', it will probably have no effect.", raw)
+ try:
+ self._exec_config_str(lhs, rhs)
+ except Exception:
+ raise ArgumentError("Invalid argument: '%s'" % raw)
+
+ elif flag_pattern.match(raw):
+ if item in flags:
+ cfg,help = flags[item]
+ self._load_flag(cfg)
+ else:
+ raise ArgumentError("Unrecognized flag: '%s'"%raw)
+ elif raw.startswith('-'):
+ kv = '--'+item
+ if kv_pattern.match(kv):
+ raise ArgumentError("Invalid argument: '%s', did you mean '%s'?"%(raw, kv))
+ else:
+ raise ArgumentError("Invalid argument: '%s'"%raw)
+ else:
+ # keep all args that aren't valid in a list,
+ # in case our parent knows what to do with them.
+ self.extra_args.append(item)
+ return self.config
+
+class ArgParseConfigLoader(CommandLineConfigLoader):
+ """A loader that uses the argparse module to load from the command line."""
+
+ def __init__(self, argv=None, aliases=None, flags=None, log=None, *parser_args, **parser_kw):
+ """Create a config loader for use with argparse.
+
+ Parameters
+ ----------
+
+ argv : optional, list
+ If given, used to read command-line arguments from, otherwise
+ sys.argv[1:] is used.
+
+ parser_args : tuple
+ A tuple of positional arguments that will be passed to the
+ constructor of :class:`argparse.ArgumentParser`.
+
+ parser_kw : dict
+ A tuple of keyword arguments that will be passed to the
+ constructor of :class:`argparse.ArgumentParser`.
+
+ Returns
+ -------
+ config : Config
+ The resulting Config object.
+ """
+ super(CommandLineConfigLoader, self).__init__(log=log)
+ self.clear()
+ if argv is None:
+ argv = sys.argv[1:]
+ self.argv = argv
+ self.aliases = aliases or {}
+ self.flags = flags or {}
+
+ self.parser_args = parser_args
+ self.version = parser_kw.pop("version", None)
+ kwargs = dict(argument_default=argparse.SUPPRESS)
+ kwargs.update(parser_kw)
+ self.parser_kw = kwargs
+
+ def load_config(self, argv=None, aliases=None, flags=None):
+ """Parse command line arguments and return as a Config object.
+
+ Parameters
+ ----------
+
+ args : optional, list
+ If given, a list with the structure of sys.argv[1:] to parse
+ arguments from. If not given, the instance's self.argv attribute
+ (given at construction time) is used."""
+ self.clear()
+ if argv is None:
+ argv = self.argv
+ if aliases is None:
+ aliases = self.aliases
+ if flags is None:
+ flags = self.flags
+ self._create_parser(aliases, flags)
+ self._parse_args(argv)
+ self._convert_to_config()
+ return self.config
+
+ def get_extra_args(self):
+ if hasattr(self, 'extra_args'):
+ return self.extra_args
+ else:
+ return []
+
+ def _create_parser(self, aliases=None, flags=None):
+ self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
+ self._add_arguments(aliases, flags)
+
+ def _add_arguments(self, aliases=None, flags=None):
+ raise NotImplementedError("subclasses must implement _add_arguments")
+
+ def _parse_args(self, args):
+ """self.parser->self.parsed_data"""
+ # decode sys.argv to support unicode command-line options
+ enc = DEFAULT_ENCODING
+ uargs = [py3compat.cast_unicode(a, enc) for a in args]
+ self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
+
+ def _convert_to_config(self):
+ """self.parsed_data->self.config"""
for k, v in vars(self.parsed_data).items():
- exec("self.config.%s = v"%k, locals(), globals())
-
-class KVArgParseConfigLoader(ArgParseConfigLoader):
- """A config loader that loads aliases and flags with argparse,
- but will use KVLoader for the rest. This allows better parsing
- of common args, such as `ipython -c 'print 5'`, but still gets
- arbitrary config with `ipython --InteractiveShell.use_readline=False`"""
-
- def _add_arguments(self, aliases=None, flags=None):
- self.alias_flags = {}
- # print aliases, flags
- if aliases is None:
- aliases = self.aliases
- if flags is None:
- flags = self.flags
- paa = self.parser.add_argument
+ exec("self.config.%s = v"%k, locals(), globals())
+
+class KVArgParseConfigLoader(ArgParseConfigLoader):
+ """A config loader that loads aliases and flags with argparse,
+ but will use KVLoader for the rest. This allows better parsing
+ of common args, such as `ipython -c 'print 5'`, but still gets
+ arbitrary config with `ipython --InteractiveShell.use_readline=False`"""
+
+ def _add_arguments(self, aliases=None, flags=None):
+ self.alias_flags = {}
+ # print aliases, flags
+ if aliases is None:
+ aliases = self.aliases
+ if flags is None:
+ flags = self.flags
+ paa = self.parser.add_argument
for key,value in aliases.items():
- if key in flags:
- # flags
- nargs = '?'
- else:
- nargs = None
- if len(key) is 1:
+ if key in flags:
+ # flags
+ nargs = '?'
+ else:
+ nargs = None
+ if len(key) is 1:
paa('-'+key, '--'+key, type=text_type, dest=value, nargs=nargs)
- else:
+ else:
paa('--'+key, type=text_type, dest=value, nargs=nargs)
for key, (value, help) in flags.items():
- if key in self.aliases:
- #
- self.alias_flags[self.aliases[key]] = value
- continue
- if len(key) is 1:
- paa('-'+key, '--'+key, action='append_const', dest='_flags', const=value)
- else:
- paa('--'+key, action='append_const', dest='_flags', const=value)
-
- def _convert_to_config(self):
- """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
- # remove subconfigs list from namespace before transforming the Namespace
- if '_flags' in self.parsed_data:
- subcs = self.parsed_data._flags
- del self.parsed_data._flags
- else:
- subcs = []
-
+ if key in self.aliases:
+ #
+ self.alias_flags[self.aliases[key]] = value
+ continue
+ if len(key) is 1:
+ paa('-'+key, '--'+key, action='append_const', dest='_flags', const=value)
+ else:
+ paa('--'+key, action='append_const', dest='_flags', const=value)
+
+ def _convert_to_config(self):
+ """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
+ # remove subconfigs list from namespace before transforming the Namespace
+ if '_flags' in self.parsed_data:
+ subcs = self.parsed_data._flags
+ del self.parsed_data._flags
+ else:
+ subcs = []
+
for k, v in vars(self.parsed_data).items():
- if v is None:
- # it was a flag that shares the name of an alias
- subcs.append(self.alias_flags[k])
- else:
- # eval the KV assignment
- self._exec_config_str(k, v)
-
- for subc in subcs:
- self._load_flag(subc)
-
- if self.extra_args:
- sub_parser = KeyValueConfigLoader(log=self.log)
- sub_parser.load_config(self.extra_args)
- self.config.merge(sub_parser.config)
- self.extra_args = sub_parser.extra_args
-
-
-def load_pyconfig_files(config_files, path):
- """Load multiple Python config files, merging each of them in turn.
-
- Parameters
- ==========
- config_files : list of str
- List of config files names to load and merge into the config.
- path : unicode
- The full path to the location of the config files.
- """
- config = Config()
- for cf in config_files:
- loader = PyFileConfigLoader(cf, path=path)
- try:
- next_config = loader.load_config()
- except ConfigFileNotFound:
- pass
- except:
- raise
- else:
- config.merge(next_config)
- return config
+ if v is None:
+ # it was a flag that shares the name of an alias
+ subcs.append(self.alias_flags[k])
+ else:
+ # eval the KV assignment
+ self._exec_config_str(k, v)
+
+ for subc in subcs:
+ self._load_flag(subc)
+
+ if self.extra_args:
+ sub_parser = KeyValueConfigLoader(log=self.log)
+ sub_parser.load_config(self.extra_args)
+ self.config.merge(sub_parser.config)
+ self.extra_args = sub_parser.extra_args
+
+
+def load_pyconfig_files(config_files, path):
+ """Load multiple Python config files, merging each of them in turn.
+
+ Parameters
+ ==========
+ config_files : list of str
+ List of config files names to load and merge into the config.
+ path : unicode
+ The full path to the location of the config files.
+ """
+ config = Config()
+ for cf in config_files:
+ loader = PyFileConfigLoader(cf, path=path)
+ try:
+ next_config = loader.load_config()
+ except ConfigFileNotFound:
+ pass
+ except:
+ raise
+ else:
+ config.merge(next_config)
+ return config
diff --git a/contrib/python/traitlets/py2/traitlets/config/manager.py b/contrib/python/traitlets/py2/traitlets/config/manager.py
index 5e5ebde9af..89dc167943 100644
--- a/contrib/python/traitlets/py2/traitlets/config/manager.py
+++ b/contrib/python/traitlets/py2/traitlets/config/manager.py
@@ -1,88 +1,88 @@
-"""Manager to read and modify config data in JSON files.
-"""
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-import errno
-import io
-import json
-import os
-
+"""Manager to read and modify config data in JSON files.
+"""
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+import errno
+import io
+import json
+import os
+
from six import PY3
-from traitlets.config import LoggingConfigurable
-from traitlets.traitlets import Unicode
-
-
-def recursive_update(target, new):
- """Recursively update one dictionary using another.
-
- None values will delete their keys.
- """
- for k, v in new.items():
- if isinstance(v, dict):
- if k not in target:
- target[k] = {}
- recursive_update(target[k], v)
- if not target[k]:
- # Prune empty subdicts
- del target[k]
-
- elif v is None:
- target.pop(k, None)
-
- else:
- target[k] = v
-
-
-class BaseJSONConfigManager(LoggingConfigurable):
- """General JSON config manager
-
- Deals with persisting/storing config in a json file
- """
-
- config_dir = Unicode('.')
-
- def ensure_config_dir_exists(self):
- try:
- os.makedirs(self.config_dir, 0o755)
- except OSError as e:
- if e.errno != errno.EEXIST:
- raise
-
- def file_name(self, section_name):
- return os.path.join(self.config_dir, section_name+'.json')
-
- def get(self, section_name):
- """Retrieve the config data for the specified section.
-
- Returns the data as a dictionary, or an empty dictionary if the file
- doesn't exist.
- """
- filename = self.file_name(section_name)
- if os.path.isfile(filename):
- with io.open(filename, encoding='utf-8') as f:
- return json.load(f)
- else:
- return {}
-
- def set(self, section_name, data):
- """Store the given config data.
- """
- filename = self.file_name(section_name)
- self.ensure_config_dir_exists()
-
- if PY3:
- f = io.open(filename, 'w', encoding='utf-8')
- else:
- f = open(filename, 'wb')
- with f:
- json.dump(data, f, indent=2)
-
- def update(self, section_name, new_data):
- """Modify the config section by recursively updating it with new_data.
-
- Returns the modified config data as a dictionary.
- """
- data = self.get(section_name)
- recursive_update(data, new_data)
- self.set(section_name, data)
- return data
+from traitlets.config import LoggingConfigurable
+from traitlets.traitlets import Unicode
+
+
+def recursive_update(target, new):
+ """Recursively update one dictionary using another.
+
+ None values will delete their keys.
+ """
+ for k, v in new.items():
+ if isinstance(v, dict):
+ if k not in target:
+ target[k] = {}
+ recursive_update(target[k], v)
+ if not target[k]:
+ # Prune empty subdicts
+ del target[k]
+
+ elif v is None:
+ target.pop(k, None)
+
+ else:
+ target[k] = v
+
+
+class BaseJSONConfigManager(LoggingConfigurable):
+ """General JSON config manager
+
+ Deals with persisting/storing config in a json file
+ """
+
+ config_dir = Unicode('.')
+
+ def ensure_config_dir_exists(self):
+ try:
+ os.makedirs(self.config_dir, 0o755)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ def file_name(self, section_name):
+ return os.path.join(self.config_dir, section_name+'.json')
+
+ def get(self, section_name):
+ """Retrieve the config data for the specified section.
+
+ Returns the data as a dictionary, or an empty dictionary if the file
+ doesn't exist.
+ """
+ filename = self.file_name(section_name)
+ if os.path.isfile(filename):
+ with io.open(filename, encoding='utf-8') as f:
+ return json.load(f)
+ else:
+ return {}
+
+ def set(self, section_name, data):
+ """Store the given config data.
+ """
+ filename = self.file_name(section_name)
+ self.ensure_config_dir_exists()
+
+ if PY3:
+ f = io.open(filename, 'w', encoding='utf-8')
+ else:
+ f = open(filename, 'wb')
+ with f:
+ json.dump(data, f, indent=2)
+
+ def update(self, section_name, new_data):
+ """Modify the config section by recursively updating it with new_data.
+
+ Returns the modified config data as a dictionary.
+ """
+ data = self.get(section_name)
+ recursive_update(data, new_data)
+ self.set(section_name, data)
+ return data
diff --git a/contrib/python/traitlets/py2/traitlets/log.py b/contrib/python/traitlets/py2/traitlets/log.py
index af86b325f5..559735bd1a 100644
--- a/contrib/python/traitlets/py2/traitlets/log.py
+++ b/contrib/python/traitlets/py2/traitlets/log.py
@@ -1,27 +1,27 @@
-"""Grab the global logger instance."""
+"""Grab the global logger instance."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import logging
+
+_logger = None
+
+def get_logger():
+ """Grab the global logger instance.
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
+ If a global Application is instantiated, grab its logger.
+ Otherwise, grab the root logger.
+ """
+ global _logger
-import logging
-
-_logger = None
-
-def get_logger():
- """Grab the global logger instance.
-
- If a global Application is instantiated, grab its logger.
- Otherwise, grab the root logger.
- """
- global _logger
-
- if _logger is None:
- from .config import Application
- if Application.initialized():
- _logger = Application.instance().log
- else:
+ if _logger is None:
+ from .config import Application
+ if Application.initialized():
+ _logger = Application.instance().log
+ else:
_logger = logging.getLogger('traitlets')
# Add a NullHandler to silence warnings about not being
# initialized, per best practice for libraries.
_logger.addHandler(logging.NullHandler())
- return _logger
+ return _logger
diff --git a/contrib/python/traitlets/py2/traitlets/traitlets.py b/contrib/python/traitlets/py2/traitlets/traitlets.py
index c07daf7400..233c047dc2 100644
--- a/contrib/python/traitlets/py2/traitlets/traitlets.py
+++ b/contrib/python/traitlets/py2/traitlets/traitlets.py
@@ -1,98 +1,98 @@
-# encoding: utf-8
-"""
-A lightweight Traits like module.
-
-This is designed to provide a lightweight, simple, pure Python version of
-many of the capabilities of enthought.traits. This includes:
-
-* Validation
-* Type specification with defaults
-* Static and dynamic notification
-* Basic predefined types
-* An API that is similar to enthought.traits
-
-We don't support:
-
-* Delegation
-* Automatic GUI generation
-* A full set of trait types. Most importantly, we don't provide container
- traits (list, dict, tuple) that can trigger notifications if their
- contents change.
-* API compatibility with enthought.traits
-
-There are also some important difference in our design:
-
-* enthought.traits does not validate default values. We do.
-
-We choose to create this module because we need these capabilities, but
-we need them to be pure Python so they work in all Python implementations,
-including Jython and IronPython.
-
-Inheritance diagram:
-
-.. inheritance-diagram:: traitlets.traitlets
- :parts: 3
-"""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-#
-# Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
-# also under the terms of the Modified BSD License.
-
-import contextlib
-import inspect
+# encoding: utf-8
+"""
+A lightweight Traits like module.
+
+This is designed to provide a lightweight, simple, pure Python version of
+many of the capabilities of enthought.traits. This includes:
+
+* Validation
+* Type specification with defaults
+* Static and dynamic notification
+* Basic predefined types
+* An API that is similar to enthought.traits
+
+We don't support:
+
+* Delegation
+* Automatic GUI generation
+* A full set of trait types. Most importantly, we don't provide container
+ traits (list, dict, tuple) that can trigger notifications if their
+ contents change.
+* API compatibility with enthought.traits
+
+There are also some important difference in our design:
+
+* enthought.traits does not validate default values. We do.
+
+We choose to create this module because we need these capabilities, but
+we need them to be pure Python so they work in all Python implementations,
+including Jython and IronPython.
+
+Inheritance diagram:
+
+.. inheritance-diagram:: traitlets.traitlets
+ :parts: 3
+"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+#
+# Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
+# also under the terms of the Modified BSD License.
+
+import contextlib
+import inspect
import os
-import re
-import sys
-import types
+import re
+import sys
+import types
import enum
-try:
- from types import ClassType, InstanceType
- ClassTypes = (ClassType, type)
-except:
- ClassTypes = (type,)
-from warnings import warn, warn_explicit
-
+try:
+ from types import ClassType, InstanceType
+ ClassTypes = (ClassType, type)
+except:
+ ClassTypes = (type,)
+from warnings import warn, warn_explicit
+
import six
-
-from .utils.getargspec import getargspec
-from .utils.importstring import import_item
-from .utils.sentinel import Sentinel
+
+from .utils.getargspec import getargspec
+from .utils.importstring import import_item
+from .utils.sentinel import Sentinel
from .utils.bunch import Bunch
-
-SequenceTypes = (list, tuple, set, frozenset)
-
-#-----------------------------------------------------------------------------
-# Basic classes
-#-----------------------------------------------------------------------------
-
-
-Undefined = Sentinel('Undefined', 'traitlets',
-'''
-Used in Traitlets to specify that no defaults are set in kwargs
-'''
-)
-
-All = Sentinel('All', 'traitlets',
-'''
-Used in Traitlets to listen to all types of notification or to notifications
-from all trait attributes.
-'''
-)
-
-# Deprecated alias
-NoDefaultSpecified = Undefined
-
-class TraitError(Exception):
- pass
-
-#-----------------------------------------------------------------------------
-# Utilities
-#-----------------------------------------------------------------------------
-
+
+SequenceTypes = (list, tuple, set, frozenset)
+
+#-----------------------------------------------------------------------------
+# Basic classes
+#-----------------------------------------------------------------------------
+
+
+Undefined = Sentinel('Undefined', 'traitlets',
+'''
+Used in Traitlets to specify that no defaults are set in kwargs
+'''
+)
+
+All = Sentinel('All', 'traitlets',
+'''
+Used in Traitlets to listen to all types of notification or to notifications
+from all trait attributes.
+'''
+)
+
+# Deprecated alias
+NoDefaultSpecified = Undefined
+
+class TraitError(Exception):
+ pass
+
+#-----------------------------------------------------------------------------
+# Utilities
+#-----------------------------------------------------------------------------
+
from ipython_genutils.py3compat import cast_unicode_py2
-
+
_name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
def isidentifier(s):
@@ -117,331 +117,331 @@ def _should_warn(key):
else:
return False
-def _deprecated_method(method, cls, method_name, msg):
- """Show deprecation warning about a magic method definition.
-
- Uses warn_explicit to bind warning to method definition instead of triggering code,
- which isn't relevant.
- """
+def _deprecated_method(method, cls, method_name, msg):
+ """Show deprecation warning about a magic method definition.
+
+ Uses warn_explicit to bind warning to method definition instead of triggering code,
+ which isn't relevant.
+ """
warn_msg = "{classname}.{method_name} is deprecated in traitlets 4.1: {msg}".format(
- classname=cls.__name__, method_name=method_name, msg=msg
- )
-
- for parent in inspect.getmro(cls):
- if method_name in parent.__dict__:
- cls = parent
- break
+ classname=cls.__name__, method_name=method_name, msg=msg
+ )
+
+ for parent in inspect.getmro(cls):
+ if method_name in parent.__dict__:
+ cls = parent
+ break
# limit deprecation messages to once per package
package_name = cls.__module__.split('.', 1)[0]
key = (package_name, msg)
if not _should_warn(key):
return
- try:
- fname = inspect.getsourcefile(method) or "<unknown>"
- lineno = inspect.getsourcelines(method)[1] or 0
+ try:
+ fname = inspect.getsourcefile(method) or "<unknown>"
+ lineno = inspect.getsourcelines(method)[1] or 0
except (IOError, TypeError) as e:
- # Failed to inspect for some reason
- warn(warn_msg + ('\n(inspection failed) %s' % e), DeprecationWarning)
- else:
- warn_explicit(warn_msg, DeprecationWarning, fname, lineno)
-
-def class_of(object):
- """ Returns a string containing the class name of an object with the
- correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
- 'a PlotValue').
- """
+ # Failed to inspect for some reason
+ warn(warn_msg + ('\n(inspection failed) %s' % e), DeprecationWarning)
+ else:
+ warn_explicit(warn_msg, DeprecationWarning, fname, lineno)
+
+def class_of(object):
+ """ Returns a string containing the class name of an object with the
+ correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
+ 'a PlotValue').
+ """
if isinstance( object, six.string_types ):
- return add_article( object )
-
- return add_article( object.__class__.__name__ )
-
-
-def add_article(name):
- """ Returns a string containing the correct indefinite article ('a' or 'an')
- prefixed to the specified string.
- """
- if name[:1].lower() in 'aeiou':
- return 'an ' + name
-
- return 'a ' + name
-
-
-def repr_type(obj):
- """ Return a string representation of a value and its type for readable
- error messages.
- """
- the_type = type(obj)
+ return add_article( object )
+
+ return add_article( object.__class__.__name__ )
+
+
+def add_article(name):
+ """ Returns a string containing the correct indefinite article ('a' or 'an')
+ prefixed to the specified string.
+ """
+ if name[:1].lower() in 'aeiou':
+ return 'an ' + name
+
+ return 'a ' + name
+
+
+def repr_type(obj):
+ """ Return a string representation of a value and its type for readable
+ error messages.
+ """
+ the_type = type(obj)
if six.PY2 and the_type is InstanceType:
- # Old-style class.
- the_type = obj.__class__
- msg = '%r %r' % (obj, the_type)
- return msg
-
-
-def is_trait(t):
- """ Returns whether the given value is an instance or subclass of TraitType.
- """
- return (isinstance(t, TraitType) or
- (isinstance(t, type) and issubclass(t, TraitType)))
-
-
-def parse_notifier_name(names):
- """Convert the name argument to a list of names.
-
- Examples
- --------
-
- >>> parse_notifier_name([])
- [All]
- >>> parse_notifier_name('a')
- ['a']
- >>> parse_notifier_name(['a', 'b'])
- ['a', 'b']
- >>> parse_notifier_name(All)
- [All]
- """
+ # Old-style class.
+ the_type = obj.__class__
+ msg = '%r %r' % (obj, the_type)
+ return msg
+
+
+def is_trait(t):
+ """ Returns whether the given value is an instance or subclass of TraitType.
+ """
+ return (isinstance(t, TraitType) or
+ (isinstance(t, type) and issubclass(t, TraitType)))
+
+
+def parse_notifier_name(names):
+ """Convert the name argument to a list of names.
+
+ Examples
+ --------
+
+ >>> parse_notifier_name([])
+ [All]
+ >>> parse_notifier_name('a')
+ ['a']
+ >>> parse_notifier_name(['a', 'b'])
+ ['a', 'b']
+ >>> parse_notifier_name(All)
+ [All]
+ """
if names is All or isinstance(names, six.string_types):
- return [names]
+ return [names]
else:
- if not names or All in names:
- return [All]
- for n in names:
+ if not names or All in names:
+ return [All]
+ for n in names:
if not isinstance(n, six.string_types):
raise TypeError("names must be strings, not %r" % n)
- return names
-
-
-class _SimpleTest:
- def __init__ ( self, value ): self.value = value
- def __call__ ( self, test ):
- return test == self.value
- def __repr__(self):
- return "<SimpleTest(%r)" % self.value
- def __str__(self):
- return self.__repr__()
-
-
-def getmembers(object, predicate=None):
- """A safe version of inspect.getmembers that handles missing attributes.
-
- This is useful when there are descriptor based attributes that for
- some reason raise AttributeError even though they exist. This happens
- in zope.inteface with the __provides__ attribute.
- """
- results = []
- for key in dir(object):
- try:
- value = getattr(object, key)
- except AttributeError:
- pass
- else:
- if not predicate or predicate(value):
- results.append((key, value))
- results.sort()
- return results
-
-def _validate_link(*tuples):
- """Validate arguments for traitlet link functions"""
- for t in tuples:
- if not len(t) == 2:
- raise TypeError("Each linked traitlet must be specified as (HasTraits, 'trait_name'), not %r" % t)
- obj, trait_name = t
- if not isinstance(obj, HasTraits):
- raise TypeError("Each object must be HasTraits, not %r" % type(obj))
- if not trait_name in obj.traits():
- raise TypeError("%r has no trait %r" % (obj, trait_name))
-
-class link(object):
- """Link traits from different objects together so they remain in sync.
-
- Parameters
- ----------
- source : (object / attribute name) pair
- target : (object / attribute name) pair
-
- Examples
- --------
-
- >>> c = link((src, 'value'), (tgt, 'value'))
- >>> src.value = 5 # updates other objects as well
- """
- updating = False
-
- def __init__(self, source, target):
- _validate_link(source, target)
- self.source, self.target = source, target
- try:
- setattr(target[0], target[1], getattr(source[0], source[1]))
- finally:
- source[0].observe(self._update_target, names=source[1])
- target[0].observe(self._update_source, names=target[1])
-
- @contextlib.contextmanager
- def _busy_updating(self):
- self.updating = True
- try:
- yield
- finally:
- self.updating = False
-
- def _update_target(self, change):
- if self.updating:
- return
- with self._busy_updating():
+ return names
+
+
+class _SimpleTest:
+ def __init__ ( self, value ): self.value = value
+ def __call__ ( self, test ):
+ return test == self.value
+ def __repr__(self):
+ return "<SimpleTest(%r)" % self.value
+ def __str__(self):
+ return self.__repr__()
+
+
+def getmembers(object, predicate=None):
+ """A safe version of inspect.getmembers that handles missing attributes.
+
+ This is useful when there are descriptor based attributes that for
+ some reason raise AttributeError even though they exist. This happens
+ in zope.inteface with the __provides__ attribute.
+ """
+ results = []
+ for key in dir(object):
+ try:
+ value = getattr(object, key)
+ except AttributeError:
+ pass
+ else:
+ if not predicate or predicate(value):
+ results.append((key, value))
+ results.sort()
+ return results
+
+def _validate_link(*tuples):
+ """Validate arguments for traitlet link functions"""
+ for t in tuples:
+ if not len(t) == 2:
+ raise TypeError("Each linked traitlet must be specified as (HasTraits, 'trait_name'), not %r" % t)
+ obj, trait_name = t
+ if not isinstance(obj, HasTraits):
+ raise TypeError("Each object must be HasTraits, not %r" % type(obj))
+ if not trait_name in obj.traits():
+ raise TypeError("%r has no trait %r" % (obj, trait_name))
+
+class link(object):
+ """Link traits from different objects together so they remain in sync.
+
+ Parameters
+ ----------
+ source : (object / attribute name) pair
+ target : (object / attribute name) pair
+
+ Examples
+ --------
+
+ >>> c = link((src, 'value'), (tgt, 'value'))
+ >>> src.value = 5 # updates other objects as well
+ """
+ updating = False
+
+ def __init__(self, source, target):
+ _validate_link(source, target)
+ self.source, self.target = source, target
+ try:
+ setattr(target[0], target[1], getattr(source[0], source[1]))
+ finally:
+ source[0].observe(self._update_target, names=source[1])
+ target[0].observe(self._update_source, names=target[1])
+
+ @contextlib.contextmanager
+ def _busy_updating(self):
+ self.updating = True
+ try:
+ yield
+ finally:
+ self.updating = False
+
+ def _update_target(self, change):
+ if self.updating:
+ return
+ with self._busy_updating():
setattr(self.target[0], self.target[1], change.new)
-
- def _update_source(self, change):
- if self.updating:
- return
- with self._busy_updating():
+
+ def _update_source(self, change):
+ if self.updating:
+ return
+ with self._busy_updating():
setattr(self.source[0], self.source[1], change.new)
-
- def unlink(self):
- self.source[0].unobserve(self._update_target, names=self.source[1])
- self.target[0].unobserve(self._update_source, names=self.target[1])
- self.source, self.target = None, None
-
-
-class directional_link(object):
- """Link the trait of a source object with traits of target objects.
-
- Parameters
- ----------
- source : (object, attribute name) pair
- target : (object, attribute name) pair
- transform: callable (optional)
- Data transformation between source and target.
-
- Examples
- --------
-
- >>> c = directional_link((src, 'value'), (tgt, 'value'))
- >>> src.value = 5 # updates target objects
- >>> tgt.value = 6 # does not update source object
- """
- updating = False
-
- def __init__(self, source, target, transform=None):
- self._transform = transform if transform else lambda x: x
- _validate_link(source, target)
- self.source, self.target = source, target
- try:
- setattr(target[0], target[1],
- self._transform(getattr(source[0], source[1])))
- finally:
- self.source[0].observe(self._update, names=self.source[1])
-
- @contextlib.contextmanager
- def _busy_updating(self):
- self.updating = True
- try:
- yield
- finally:
- self.updating = False
-
- def _update(self, change):
- if self.updating:
- return
- with self._busy_updating():
- setattr(self.target[0], self.target[1],
+
+ def unlink(self):
+ self.source[0].unobserve(self._update_target, names=self.source[1])
+ self.target[0].unobserve(self._update_source, names=self.target[1])
+ self.source, self.target = None, None
+
+
+class directional_link(object):
+ """Link the trait of a source object with traits of target objects.
+
+ Parameters
+ ----------
+ source : (object, attribute name) pair
+ target : (object, attribute name) pair
+ transform: callable (optional)
+ Data transformation between source and target.
+
+ Examples
+ --------
+
+ >>> c = directional_link((src, 'value'), (tgt, 'value'))
+ >>> src.value = 5 # updates target objects
+ >>> tgt.value = 6 # does not update source object
+ """
+ updating = False
+
+ def __init__(self, source, target, transform=None):
+ self._transform = transform if transform else lambda x: x
+ _validate_link(source, target)
+ self.source, self.target = source, target
+ try:
+ setattr(target[0], target[1],
+ self._transform(getattr(source[0], source[1])))
+ finally:
+ self.source[0].observe(self._update, names=self.source[1])
+
+ @contextlib.contextmanager
+ def _busy_updating(self):
+ self.updating = True
+ try:
+ yield
+ finally:
+ self.updating = False
+
+ def _update(self, change):
+ if self.updating:
+ return
+ with self._busy_updating():
+ setattr(self.target[0], self.target[1],
self._transform(change.new))
-
- def unlink(self):
- self.source[0].unobserve(self._update, names=self.source[1])
- self.source, self.target = None, None
-
-dlink = directional_link
-
-
-#-----------------------------------------------------------------------------
+
+ def unlink(self):
+ self.source[0].unobserve(self._update, names=self.source[1])
+ self.source, self.target = None, None
+
+dlink = directional_link
+
+
+#-----------------------------------------------------------------------------
# Base Descriptor Class
-#-----------------------------------------------------------------------------
-
-
-class BaseDescriptor(object):
- """Base descriptor class
-
- Notes
- -----
- This implements Python's descriptor prototol.
-
- This class is the base class for all such descriptors. The
- only magic we use is a custom metaclass for the main :class:`HasTraits`
- class that does the following:
-
- 1. Sets the :attr:`name` attribute of every :class:`BaseDescriptor`
- instance in the class dict to the name of the attribute.
- 2. Sets the :attr:`this_class` attribute of every :class:`BaseDescriptor`
- instance in the class dict to the *class* that declared the trait.
- This is used by the :class:`This` trait to allow subclasses to
- accept superclasses for :class:`This` values.
- """
-
- name = None
- this_class = None
-
- def class_init(self, cls, name):
- """Part of the initialization which may depend on the underlying
- HasDescriptors class.
-
- It is typically overloaded for specific types.
-
- This method is called by :meth:`MetaHasDescriptors.__init__`
- passing the class (`cls`) and `name` under which the descriptor
- has been assigned.
- """
- self.this_class = cls
- self.name = name
-
- def instance_init(self, obj):
- """Part of the initialization which may depend on the underlying
- HasDescriptors instance.
-
- It is typically overloaded for specific types.
-
- This method is called by :meth:`HasTraits.__new__` and in the
- :meth:`BaseDescriptor.instance_init` method of descriptors holding
- other descriptors.
- """
- pass
-
-
-class TraitType(BaseDescriptor):
- """A base class for all trait types.
- """
-
- metadata = {}
- default_value = Undefined
- allow_none = False
- read_only = False
- info_text = 'any value'
-
+#-----------------------------------------------------------------------------
+
+
+class BaseDescriptor(object):
+ """Base descriptor class
+
+ Notes
+ -----
+ This implements Python's descriptor prototol.
+
+ This class is the base class for all such descriptors. The
+ only magic we use is a custom metaclass for the main :class:`HasTraits`
+ class that does the following:
+
+ 1. Sets the :attr:`name` attribute of every :class:`BaseDescriptor`
+ instance in the class dict to the name of the attribute.
+ 2. Sets the :attr:`this_class` attribute of every :class:`BaseDescriptor`
+ instance in the class dict to the *class* that declared the trait.
+ This is used by the :class:`This` trait to allow subclasses to
+ accept superclasses for :class:`This` values.
+ """
+
+ name = None
+ this_class = None
+
+ def class_init(self, cls, name):
+ """Part of the initialization which may depend on the underlying
+ HasDescriptors class.
+
+ It is typically overloaded for specific types.
+
+ This method is called by :meth:`MetaHasDescriptors.__init__`
+ passing the class (`cls`) and `name` under which the descriptor
+ has been assigned.
+ """
+ self.this_class = cls
+ self.name = name
+
+ def instance_init(self, obj):
+ """Part of the initialization which may depend on the underlying
+ HasDescriptors instance.
+
+ It is typically overloaded for specific types.
+
+ This method is called by :meth:`HasTraits.__new__` and in the
+ :meth:`BaseDescriptor.instance_init` method of descriptors holding
+ other descriptors.
+ """
+ pass
+
+
+class TraitType(BaseDescriptor):
+ """A base class for all trait types.
+ """
+
+ metadata = {}
+ default_value = Undefined
+ allow_none = False
+ read_only = False
+ info_text = 'any value'
+
def __init__(self, default_value=Undefined, allow_none=False, read_only=None, help=None,
config=None, **kwargs):
- """Declare a traitlet.
-
- If *allow_none* is True, None is a valid value in addition to any
- values that are normally valid. The default is up to the subclass.
- For most trait types, the default value for ``allow_none`` is False.
-
- Extra metadata can be associated with the traitlet using the .tag() convenience method
- or by using the traitlet instance's .metadata dictionary.
- """
- if default_value is not Undefined:
- self.default_value = default_value
+ """Declare a traitlet.
+
+ If *allow_none* is True, None is a valid value in addition to any
+ values that are normally valid. The default is up to the subclass.
+ For most trait types, the default value for ``allow_none`` is False.
+
+ Extra metadata can be associated with the traitlet using the .tag() convenience method
+ or by using the traitlet instance's .metadata dictionary.
+ """
+ if default_value is not Undefined:
+ self.default_value = default_value
if allow_none:
- self.allow_none = allow_none
- if read_only is not None:
- self.read_only = read_only
- self.help = help if help is not None else ''
-
+ self.allow_none = allow_none
+ if read_only is not None:
+ self.read_only = read_only
+ self.help = help if help is not None else ''
+
if len(kwargs) > 0:
- stacklevel = 1
- f = inspect.currentframe()
- # count supers to determine stacklevel for warning
- while f.f_code.co_name == '__init__':
- stacklevel += 1
- f = f.f_back
+ stacklevel = 1
+ f = inspect.currentframe()
+ # count supers to determine stacklevel for warning
+ while f.f_code.co_name == '__init__':
+ stacklevel += 1
+ f = f.f_back
mod = f.f_globals.get('__name__') or ''
pkg = mod.split('.', 1)[0]
key = tuple(['metadata-tag', pkg] + sorted(kwargs))
@@ -450,365 +450,365 @@ class TraitType(BaseDescriptor):
"With traitlets 4.1, metadata should be set using the .tag() method, "
"e.g., Int().tag(key1='value1', key2='value2')" % (kwargs,),
DeprecationWarning, stacklevel=stacklevel)
- if len(self.metadata) > 0:
- self.metadata = self.metadata.copy()
+ if len(self.metadata) > 0:
+ self.metadata = self.metadata.copy()
self.metadata.update(kwargs)
- else:
+ else:
self.metadata = kwargs
- else:
- self.metadata = self.metadata.copy()
+ else:
+ self.metadata = self.metadata.copy()
if config is not None:
self.metadata['config'] = config
-
- # We add help to the metadata during a deprecation period so that
- # code that looks for the help string there can find it.
- if help is not None:
- self.metadata['help'] = help
-
- def get_default_value(self):
- """DEPRECATED: Retrieve the static default value for this trait.
-
- Use self.default_value instead
- """
+
+ # We add help to the metadata during a deprecation period so that
+ # code that looks for the help string there can find it.
+ if help is not None:
+ self.metadata['help'] = help
+
+ def get_default_value(self):
+ """DEPRECATED: Retrieve the static default value for this trait.
+
+ Use self.default_value instead
+ """
warn("get_default_value is deprecated in traitlets 4.0: use the .default_value attribute", DeprecationWarning,
- stacklevel=2)
- return self.default_value
-
- def init_default_value(self, obj):
- """DEPRECATED: Set the static default value for the trait type.
- """
+ stacklevel=2)
+ return self.default_value
+
+ def init_default_value(self, obj):
+ """DEPRECATED: Set the static default value for the trait type.
+ """
warn("init_default_value is deprecated in traitlets 4.0, and may be removed in the future", DeprecationWarning,
- stacklevel=2)
- value = self._validate(obj, self.default_value)
- obj._trait_values[self.name] = value
- return value
-
- def _dynamic_default_callable(self, obj):
- """Retrieve a callable to calculate the default for this traitlet.
-
- This looks for:
-
+ stacklevel=2)
+ value = self._validate(obj, self.default_value)
+ obj._trait_values[self.name] = value
+ return value
+
+ def _dynamic_default_callable(self, obj):
+ """Retrieve a callable to calculate the default for this traitlet.
+
+ This looks for:
+
* default generators registered with the @default descriptor.
- * obj._{name}_default() on the class with the traitlet, or a subclass
- that obj belongs to.
- * trait.make_dynamic_default, which is defined by Instance
-
- If neither exist, it returns None
- """
- # Traitlets without a name are not on the instance, e.g. in List or Union
- if self.name:
-
- # Only look for default handlers in classes derived from self.this_class.
- mro = type(obj).mro()
- meth_name = '_%s_default' % self.name
- for cls in mro[:mro.index(self.this_class) + 1]:
- if hasattr(cls, '_trait_default_generators'):
- default_handler = cls._trait_default_generators.get(self.name)
- if default_handler is not None and default_handler.this_class == cls:
- return types.MethodType(default_handler.func, obj)
-
- if meth_name in cls.__dict__:
- method = getattr(obj, meth_name)
- return method
-
- return getattr(self, 'make_dynamic_default', None)
-
- def instance_init(self, obj):
- # If no dynamic initialiser is present, and the trait implementation or
- # use provides a static default, transfer that to obj._trait_values.
+ * obj._{name}_default() on the class with the traitlet, or a subclass
+ that obj belongs to.
+ * trait.make_dynamic_default, which is defined by Instance
+
+ If neither exist, it returns None
+ """
+ # Traitlets without a name are not on the instance, e.g. in List or Union
+ if self.name:
+
+ # Only look for default handlers in classes derived from self.this_class.
+ mro = type(obj).mro()
+ meth_name = '_%s_default' % self.name
+ for cls in mro[:mro.index(self.this_class) + 1]:
+ if hasattr(cls, '_trait_default_generators'):
+ default_handler = cls._trait_default_generators.get(self.name)
+ if default_handler is not None and default_handler.this_class == cls:
+ return types.MethodType(default_handler.func, obj)
+
+ if meth_name in cls.__dict__:
+ method = getattr(obj, meth_name)
+ return method
+
+ return getattr(self, 'make_dynamic_default', None)
+
+ def instance_init(self, obj):
+ # If no dynamic initialiser is present, and the trait implementation or
+ # use provides a static default, transfer that to obj._trait_values.
with obj.cross_validation_lock:
if (self._dynamic_default_callable(obj) is None) \
and (self.default_value is not Undefined):
v = self._validate(obj, self.default_value)
if self.name is not None:
obj._trait_values[self.name] = v
-
+
def get(self, obj, cls=None):
- try:
- value = obj._trait_values[self.name]
- except KeyError:
- # Check for a dynamic initializer.
- dynamic_default = self._dynamic_default_callable(obj)
- if dynamic_default is None:
- raise TraitError("No default value found for %s trait of %r"
- % (self.name, obj))
- value = self._validate(obj, dynamic_default())
- obj._trait_values[self.name] = value
- return value
- except Exception:
- # This should never be reached.
- raise TraitError('Unexpected error in TraitType: '
- 'default value not set properly')
- else:
- return value
-
- def __get__(self, obj, cls=None):
- """Get the value of the trait by self.name for the instance.
-
- Default values are instantiated when :meth:`HasTraits.__new__`
- is called. Thus by the time this method gets called either the
- default value or a user defined value (they called :meth:`__set__`)
- is in the :class:`HasTraits` instance.
- """
- if obj is None:
- return self
- else:
- return self.get(obj, cls)
-
- def set(self, obj, value):
- new_value = self._validate(obj, value)
- try:
- old_value = obj._trait_values[self.name]
- except KeyError:
- old_value = self.default_value
-
- obj._trait_values[self.name] = new_value
- try:
- silent = bool(old_value == new_value)
- except:
- # if there is an error in comparing, default to notify
- silent = False
- if silent is not True:
- # we explicitly compare silent to True just in case the equality
- # comparison above returns something other than True/False
- obj._notify_trait(self.name, old_value, new_value)
-
- def __set__(self, obj, value):
- """Set the value of the trait by self.name for the instance.
-
- Values pass through a validation stage where errors are raised when
- impropper types, or types that cannot be coerced, are encountered.
- """
- if self.read_only:
- raise TraitError('The "%s" trait is read-only.' % self.name)
- else:
- self.set(obj, value)
-
- def _validate(self, obj, value):
- if value is None and self.allow_none:
- return value
- if hasattr(self, 'validate'):
- value = self.validate(obj, value)
- if obj._cross_validation_lock is False:
- value = self._cross_validate(obj, value)
- return value
-
- def _cross_validate(self, obj, value):
- if self.name in obj._trait_validators:
+ try:
+ value = obj._trait_values[self.name]
+ except KeyError:
+ # Check for a dynamic initializer.
+ dynamic_default = self._dynamic_default_callable(obj)
+ if dynamic_default is None:
+ raise TraitError("No default value found for %s trait of %r"
+ % (self.name, obj))
+ value = self._validate(obj, dynamic_default())
+ obj._trait_values[self.name] = value
+ return value
+ except Exception:
+ # This should never be reached.
+ raise TraitError('Unexpected error in TraitType: '
+ 'default value not set properly')
+ else:
+ return value
+
+ def __get__(self, obj, cls=None):
+ """Get the value of the trait by self.name for the instance.
+
+ Default values are instantiated when :meth:`HasTraits.__new__`
+ is called. Thus by the time this method gets called either the
+ default value or a user defined value (they called :meth:`__set__`)
+ is in the :class:`HasTraits` instance.
+ """
+ if obj is None:
+ return self
+ else:
+ return self.get(obj, cls)
+
+ def set(self, obj, value):
+ new_value = self._validate(obj, value)
+ try:
+ old_value = obj._trait_values[self.name]
+ except KeyError:
+ old_value = self.default_value
+
+ obj._trait_values[self.name] = new_value
+ try:
+ silent = bool(old_value == new_value)
+ except:
+ # if there is an error in comparing, default to notify
+ silent = False
+ if silent is not True:
+ # we explicitly compare silent to True just in case the equality
+ # comparison above returns something other than True/False
+ obj._notify_trait(self.name, old_value, new_value)
+
+ def __set__(self, obj, value):
+ """Set the value of the trait by self.name for the instance.
+
+ Values pass through a validation stage where errors are raised when
+ impropper types, or types that cannot be coerced, are encountered.
+ """
+ if self.read_only:
+ raise TraitError('The "%s" trait is read-only.' % self.name)
+ else:
+ self.set(obj, value)
+
+ def _validate(self, obj, value):
+ if value is None and self.allow_none:
+ return value
+ if hasattr(self, 'validate'):
+ value = self.validate(obj, value)
+ if obj._cross_validation_lock is False:
+ value = self._cross_validate(obj, value)
+ return value
+
+ def _cross_validate(self, obj, value):
+ if self.name in obj._trait_validators:
proposal = Bunch({'trait': self, 'value': value, 'owner': obj})
- value = obj._trait_validators[self.name](obj, proposal)
- elif hasattr(obj, '_%s_validate' % self.name):
- meth_name = '_%s_validate' % self.name
- cross_validate = getattr(obj, meth_name)
- _deprecated_method(cross_validate, obj.__class__, meth_name,
- "use @validate decorator instead.")
- value = cross_validate(value, self)
- return value
-
- def __or__(self, other):
- if isinstance(other, Union):
- return Union([self] + other.trait_types)
- else:
- return Union([self, other])
-
- def info(self):
- return self.info_text
-
- def error(self, obj, value):
- if obj is not None:
- e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
- % (self.name, class_of(obj),
- self.info(), repr_type(value))
- else:
- e = "The '%s' trait must be %s, but a value of %r was specified." \
- % (self.name, self.info(), repr_type(value))
- raise TraitError(e)
-
- def get_metadata(self, key, default=None):
- """DEPRECATED: Get a metadata value.
-
- Use .metadata[key] or .metadata.get(key, default) instead.
- """
- if key == 'help':
- msg = "use the instance .help string directly, like x.help"
- else:
- msg = "use the instance .metadata dictionary directly, like x.metadata[key] or x.metadata.get(key, default)"
+ value = obj._trait_validators[self.name](obj, proposal)
+ elif hasattr(obj, '_%s_validate' % self.name):
+ meth_name = '_%s_validate' % self.name
+ cross_validate = getattr(obj, meth_name)
+ _deprecated_method(cross_validate, obj.__class__, meth_name,
+ "use @validate decorator instead.")
+ value = cross_validate(value, self)
+ return value
+
+ def __or__(self, other):
+ if isinstance(other, Union):
+ return Union([self] + other.trait_types)
+ else:
+ return Union([self, other])
+
+ def info(self):
+ return self.info_text
+
+ def error(self, obj, value):
+ if obj is not None:
+ e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
+ % (self.name, class_of(obj),
+ self.info(), repr_type(value))
+ else:
+ e = "The '%s' trait must be %s, but a value of %r was specified." \
+ % (self.name, self.info(), repr_type(value))
+ raise TraitError(e)
+
+ def get_metadata(self, key, default=None):
+ """DEPRECATED: Get a metadata value.
+
+ Use .metadata[key] or .metadata.get(key, default) instead.
+ """
+ if key == 'help':
+ msg = "use the instance .help string directly, like x.help"
+ else:
+ msg = "use the instance .metadata dictionary directly, like x.metadata[key] or x.metadata.get(key, default)"
warn("Deprecated in traitlets 4.1, " + msg, DeprecationWarning, stacklevel=2)
- return self.metadata.get(key, default)
-
- def set_metadata(self, key, value):
- """DEPRECATED: Set a metadata key/value.
-
- Use .metadata[key] = value instead.
- """
- if key == 'help':
- msg = "use the instance .help string directly, like x.help = value"
- else:
- msg = "use the instance .metadata dictionary directly, like x.metadata[key] = value"
+ return self.metadata.get(key, default)
+
+ def set_metadata(self, key, value):
+ """DEPRECATED: Set a metadata key/value.
+
+ Use .metadata[key] = value instead.
+ """
+ if key == 'help':
+ msg = "use the instance .help string directly, like x.help = value"
+ else:
+ msg = "use the instance .metadata dictionary directly, like x.metadata[key] = value"
warn("Deprecated in traitlets 4.1, " + msg, DeprecationWarning, stacklevel=2)
- self.metadata[key] = value
-
- def tag(self, **metadata):
- """Sets metadata and returns self.
-
- This allows convenient metadata tagging when initializing the trait, such as:
-
- >>> Int(0).tag(config=True, sync=True)
- """
+ self.metadata[key] = value
+
+ def tag(self, **metadata):
+ """Sets metadata and returns self.
+
+ This allows convenient metadata tagging when initializing the trait, such as:
+
+ >>> Int(0).tag(config=True, sync=True)
+ """
maybe_constructor_keywords = set(metadata.keys()).intersection({'help','allow_none', 'read_only', 'default_value'})
if maybe_constructor_keywords:
warn('The following attributes are set in using `tag`, but seem to be constructor keywords arguments: %s '%
maybe_constructor_keywords, UserWarning, stacklevel=2)
- self.metadata.update(metadata)
- return self
-
- def default_value_repr(self):
- return repr(self.default_value)
-
-#-----------------------------------------------------------------------------
-# The HasTraits implementation
-#-----------------------------------------------------------------------------
-
-class _CallbackWrapper(object):
- """An object adapting a on_trait_change callback into an observe callback.
-
- The comparison operator __eq__ is implemented to enable removal of wrapped
- callbacks.
- """
-
- def __init__(self, cb):
- self.cb = cb
- # Bound methods have an additional 'self' argument.
- offset = -1 if isinstance(self.cb, types.MethodType) else 0
- self.nargs = len(getargspec(cb)[0]) + offset
- if (self.nargs > 4):
- raise TraitError('a trait changed callback must have 0-4 arguments.')
-
- def __eq__(self, other):
- # The wrapper is equal to the wrapped element
- if isinstance(other, _CallbackWrapper):
- return self.cb == other.cb
- else:
- return self.cb == other
-
- def __call__(self, change):
- # The wrapper is callable
- if self.nargs == 0:
- self.cb()
- elif self.nargs == 1:
+ self.metadata.update(metadata)
+ return self
+
+ def default_value_repr(self):
+ return repr(self.default_value)
+
+#-----------------------------------------------------------------------------
+# The HasTraits implementation
+#-----------------------------------------------------------------------------
+
+class _CallbackWrapper(object):
+ """An object adapting a on_trait_change callback into an observe callback.
+
+ The comparison operator __eq__ is implemented to enable removal of wrapped
+ callbacks.
+ """
+
+ def __init__(self, cb):
+ self.cb = cb
+ # Bound methods have an additional 'self' argument.
+ offset = -1 if isinstance(self.cb, types.MethodType) else 0
+ self.nargs = len(getargspec(cb)[0]) + offset
+ if (self.nargs > 4):
+ raise TraitError('a trait changed callback must have 0-4 arguments.')
+
+ def __eq__(self, other):
+ # The wrapper is equal to the wrapped element
+ if isinstance(other, _CallbackWrapper):
+ return self.cb == other.cb
+ else:
+ return self.cb == other
+
+ def __call__(self, change):
+ # The wrapper is callable
+ if self.nargs == 0:
+ self.cb()
+ elif self.nargs == 1:
self.cb(change.name)
- elif self.nargs == 2:
+ elif self.nargs == 2:
self.cb(change.name, change.new)
- elif self.nargs == 3:
+ elif self.nargs == 3:
self.cb(change.name, change.old, change.new)
- elif self.nargs == 4:
+ elif self.nargs == 4:
self.cb(change.name, change.old, change.new, change.owner)
-
-def _callback_wrapper(cb):
- if isinstance(cb, _CallbackWrapper):
- return cb
- else:
- return _CallbackWrapper(cb)
-
-
-class MetaHasDescriptors(type):
- """A metaclass for HasDescriptors.
-
- This metaclass makes sure that any TraitType class attributes are
- instantiated and sets their name attribute.
- """
-
- def __new__(mcls, name, bases, classdict):
- """Create the HasDescriptors class."""
+
+def _callback_wrapper(cb):
+ if isinstance(cb, _CallbackWrapper):
+ return cb
+ else:
+ return _CallbackWrapper(cb)
+
+
+class MetaHasDescriptors(type):
+ """A metaclass for HasDescriptors.
+
+ This metaclass makes sure that any TraitType class attributes are
+ instantiated and sets their name attribute.
+ """
+
+ def __new__(mcls, name, bases, classdict):
+ """Create the HasDescriptors class."""
for k, v in classdict.items():
- # ----------------------------------------------------------------
- # Support of deprecated behavior allowing for TraitType types
- # to be used instead of TraitType instances.
- if inspect.isclass(v) and issubclass(v, TraitType):
+ # ----------------------------------------------------------------
+ # Support of deprecated behavior allowing for TraitType types
+ # to be used instead of TraitType instances.
+ if inspect.isclass(v) and issubclass(v, TraitType):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)."
" Passing types is deprecated in traitlets 4.1.",
- DeprecationWarning, stacklevel=2)
- classdict[k] = v()
- # ----------------------------------------------------------------
-
- return super(MetaHasDescriptors, mcls).__new__(mcls, name, bases, classdict)
-
- def __init__(cls, name, bases, classdict):
- """Finish initializing the HasDescriptors class."""
- super(MetaHasDescriptors, cls).__init__(name, bases, classdict)
- cls.setup_class(classdict)
-
- def setup_class(cls, classdict):
- """Setup descriptor instance on the class
-
- This sets the :attr:`this_class` and :attr:`name` attributes of each
- BaseDescriptor in the class dict of the newly created ``cls`` before
- calling their :attr:`class_init` method.
- """
+ DeprecationWarning, stacklevel=2)
+ classdict[k] = v()
+ # ----------------------------------------------------------------
+
+ return super(MetaHasDescriptors, mcls).__new__(mcls, name, bases, classdict)
+
+ def __init__(cls, name, bases, classdict):
+ """Finish initializing the HasDescriptors class."""
+ super(MetaHasDescriptors, cls).__init__(name, bases, classdict)
+ cls.setup_class(classdict)
+
+ def setup_class(cls, classdict):
+ """Setup descriptor instance on the class
+
+ This sets the :attr:`this_class` and :attr:`name` attributes of each
+ BaseDescriptor in the class dict of the newly created ``cls`` before
+ calling their :attr:`class_init` method.
+ """
for k, v in classdict.items():
- if isinstance(v, BaseDescriptor):
- v.class_init(cls, k)
-
-
-class MetaHasTraits(MetaHasDescriptors):
- """A metaclass for HasTraits."""
-
- def setup_class(cls, classdict):
- cls._trait_default_generators = {}
- super(MetaHasTraits, cls).setup_class(classdict)
-
-
-def observe(*names, **kwargs):
- """A decorator which can be used to observe Traits on a class.
-
+ if isinstance(v, BaseDescriptor):
+ v.class_init(cls, k)
+
+
+class MetaHasTraits(MetaHasDescriptors):
+ """A metaclass for HasTraits."""
+
+ def setup_class(cls, classdict):
+ cls._trait_default_generators = {}
+ super(MetaHasTraits, cls).setup_class(classdict)
+
+
+def observe(*names, **kwargs):
+ """A decorator which can be used to observe Traits on a class.
+
The handler passed to the decorator will be called with one ``change``
dict argument. The change dictionary at least holds a 'type' key and a
'name' key, corresponding respectively to the type of notification and the
name of the attribute that triggered the notification.
-
- Other keys may be passed depending on the value of 'type'. In the case
- where type is 'change', we also have the following keys:
- * ``owner`` : the HasTraits instance
- * ``old`` : the old value of the modified trait attribute
- * ``new`` : the new value of the modified trait attribute
- * ``name`` : the name of the modified trait attribute.
-
- Parameters
- ----------
- *names
- The str names of the Traits to observe on the object.
+
+ Other keys may be passed depending on the value of 'type'. In the case
+ where type is 'change', we also have the following keys:
+ * ``owner`` : the HasTraits instance
+ * ``old`` : the old value of the modified trait attribute
+ * ``new`` : the new value of the modified trait attribute
+ * ``name`` : the name of the modified trait attribute.
+
+ Parameters
+ ----------
+ *names
+ The str names of the Traits to observe on the object.
type: str, kwarg-only
The type of event to observe (e.g. 'change')
- """
+ """
if not names:
raise TypeError("Please specify at least one trait name to observe.")
for name in names:
if name is not All and not isinstance(name, six.string_types):
raise TypeError("trait names to observe must be strings or All, not %r" % name)
- return ObserveHandler(names, type=kwargs.get('type', 'change'))
-
-
-def observe_compat(func):
- """Backward-compatibility shim decorator for observers
-
- Use with:
-
- @observe('name')
- @observe_compat
- def _foo_changed(self, change):
- ...
-
- With this, `super()._foo_changed(self, name, old, new)` in subclasses will still work.
- Allows adoption of new observer API without breaking subclasses that override and super.
- """
- def compatible_observer(self, change_or_name, old=Undefined, new=Undefined):
- if isinstance(change_or_name, dict):
- change = change_or_name
- else:
- clsname = self.__class__.__name__
+ return ObserveHandler(names, type=kwargs.get('type', 'change'))
+
+
+def observe_compat(func):
+ """Backward-compatibility shim decorator for observers
+
+ Use with:
+
+ @observe('name')
+ @observe_compat
+ def _foo_changed(self, change):
+ ...
+
+ With this, `super()._foo_changed(self, name, old, new)` in subclasses will still work.
+ Allows adoption of new observer API without breaking subclasses that override and super.
+ """
+ def compatible_observer(self, change_or_name, old=Undefined, new=Undefined):
+ if isinstance(change_or_name, dict):
+ change = change_or_name
+ else:
+ clsname = self.__class__.__name__
warn("A parent of %s._%s_changed has adopted the new (traitlets 4.1) @observe(change) API" % (
- clsname, change_or_name), DeprecationWarning)
+ clsname, change_or_name), DeprecationWarning)
change = Bunch(
type='change',
old=old,
@@ -816,182 +816,182 @@ def observe_compat(func):
name=change_or_name,
owner=self,
)
- return func(self, change)
- return compatible_observer
-
-
-def validate(*names):
- """A decorator to register cross validator of HasTraits object's state
- when a Trait is set.
-
- The handler passed to the decorator must have one ``proposal`` dict argument.
- The proposal dictionary must hold the following keys:
- * ``owner`` : the HasTraits instance
- * ``value`` : the proposed value for the modified trait attribute
- * ``trait`` : the TraitType instance associated with the attribute
-
- Parameters
- ----------
- names
- The str names of the Traits to validate.
-
- Notes
- -----
+ return func(self, change)
+ return compatible_observer
+
+
+def validate(*names):
+ """A decorator to register cross validator of HasTraits object's state
+ when a Trait is set.
+
+ The handler passed to the decorator must have one ``proposal`` dict argument.
+ The proposal dictionary must hold the following keys:
+ * ``owner`` : the HasTraits instance
+ * ``value`` : the proposed value for the modified trait attribute
+ * ``trait`` : the TraitType instance associated with the attribute
+
+ Parameters
+ ----------
+ names
+ The str names of the Traits to validate.
+
+ Notes
+ -----
Since the owner has access to the ``HasTraits`` instance via the 'owner' key,
- the registered cross validator could potentially make changes to attributes
- of the ``HasTraits`` instance. However, we recommend not to do so. The reason
- is that the cross-validation of attributes may run in arbitrary order when
+ the registered cross validator could potentially make changes to attributes
+ of the ``HasTraits`` instance. However, we recommend not to do so. The reason
+ is that the cross-validation of attributes may run in arbitrary order when
exiting the ``hold_trait_notifications`` context, and such changes may not
- commute.
- """
+ commute.
+ """
if not names:
raise TypeError("Please specify at least one trait name to validate.")
for name in names:
if name is not All and not isinstance(name, six.string_types):
raise TypeError("trait names to validate must be strings or All, not %r" % name)
- return ValidateHandler(names)
-
-
-def default(name):
- """ A decorator which assigns a dynamic default for a Trait on a HasTraits object.
-
- Parameters
- ----------
- name
- The str name of the Trait on the object whose default should be generated.
-
- Notes
- -----
- Unlike observers and validators which are properties of the HasTraits
- instance, default value generators are class-level properties.
-
- Besides, default generators are only invoked if they are registered in
- subclasses of `this_type`.
-
- ::
-
- class A(HasTraits):
- bar = Int()
-
- @default('bar')
- def get_bar_default(self):
- return 11
-
-
- class B(A):
- bar = Float() # This trait ignores the default generator defined in
- # the base class A
-
-
- class C(B):
-
- @default('bar')
- def some_other_default(self): # This default generator should not be
- return 3.0 # ignored since it is defined in a
- # class derived from B.a.this_class.
- """
+ return ValidateHandler(names)
+
+
+def default(name):
+ """ A decorator which assigns a dynamic default for a Trait on a HasTraits object.
+
+ Parameters
+ ----------
+ name
+ The str name of the Trait on the object whose default should be generated.
+
+ Notes
+ -----
+ Unlike observers and validators which are properties of the HasTraits
+ instance, default value generators are class-level properties.
+
+ Besides, default generators are only invoked if they are registered in
+ subclasses of `this_type`.
+
+ ::
+
+ class A(HasTraits):
+ bar = Int()
+
+ @default('bar')
+ def get_bar_default(self):
+ return 11
+
+
+ class B(A):
+ bar = Float() # This trait ignores the default generator defined in
+ # the base class A
+
+
+ class C(B):
+
+ @default('bar')
+ def some_other_default(self): # This default generator should not be
+ return 3.0 # ignored since it is defined in a
+ # class derived from B.a.this_class.
+ """
if not isinstance(name, six.string_types):
raise TypeError("Trait name must be a string or All, not %r" % name)
- return DefaultHandler(name)
-
-
-class EventHandler(BaseDescriptor):
-
- def _init_call(self, func):
- self.func = func
- return self
-
- def __call__(self, *args, **kwargs):
+ return DefaultHandler(name)
+
+
+class EventHandler(BaseDescriptor):
+
+ def _init_call(self, func):
+ self.func = func
+ return self
+
+ def __call__(self, *args, **kwargs):
"""Pass `*args` and `**kwargs` to the handler's function if it exists."""
- if hasattr(self, 'func'):
- return self.func(*args, **kwargs)
- else:
- return self._init_call(*args, **kwargs)
-
- def __get__(self, inst, cls=None):
- if inst is None:
- return self
- return types.MethodType(self.func, inst)
-
-
-class ObserveHandler(EventHandler):
-
- def __init__(self, names, type):
- self.trait_names = names
- self.type = type
-
- def instance_init(self, inst):
- inst.observe(self, self.trait_names, type=self.type)
-
-
-class ValidateHandler(EventHandler):
-
- def __init__(self, names):
- self.trait_names = names
-
- def instance_init(self, inst):
- inst._register_validator(self, self.trait_names)
-
-
-class DefaultHandler(EventHandler):
-
- def __init__(self, name):
- self.trait_name = name
-
- def class_init(self, cls, name):
- super(DefaultHandler, self).class_init(cls, name)
- cls._trait_default_generators[self.trait_name] = self
-
-
+ if hasattr(self, 'func'):
+ return self.func(*args, **kwargs)
+ else:
+ return self._init_call(*args, **kwargs)
+
+ def __get__(self, inst, cls=None):
+ if inst is None:
+ return self
+ return types.MethodType(self.func, inst)
+
+
+class ObserveHandler(EventHandler):
+
+ def __init__(self, names, type):
+ self.trait_names = names
+ self.type = type
+
+ def instance_init(self, inst):
+ inst.observe(self, self.trait_names, type=self.type)
+
+
+class ValidateHandler(EventHandler):
+
+ def __init__(self, names):
+ self.trait_names = names
+
+ def instance_init(self, inst):
+ inst._register_validator(self, self.trait_names)
+
+
+class DefaultHandler(EventHandler):
+
+ def __init__(self, name):
+ self.trait_name = name
+
+ def class_init(self, cls, name):
+ super(DefaultHandler, self).class_init(cls, name)
+ cls._trait_default_generators[self.trait_name] = self
+
+
class HasDescriptors(six.with_metaclass(MetaHasDescriptors, object)):
- """The base class for all classes that have descriptors.
- """
-
+ """The base class for all classes that have descriptors.
+ """
+
def __new__(cls, *args, **kwargs):
- # This is needed because object.__new__ only accepts
- # the cls argument.
- new_meth = super(HasDescriptors, cls).__new__
- if new_meth is object.__new__:
- inst = new_meth(cls)
- else:
+ # This is needed because object.__new__ only accepts
+ # the cls argument.
+ new_meth = super(HasDescriptors, cls).__new__
+ if new_meth is object.__new__:
+ inst = new_meth(cls)
+ else:
inst = new_meth(cls, *args, **kwargs)
inst.setup_instance(*args, **kwargs)
- return inst
-
+ return inst
+
def setup_instance(self, *args, **kwargs):
"""
This is called **before** self.__init__ is called.
"""
self._cross_validation_lock = False
- cls = self.__class__
- for key in dir(cls):
- # Some descriptors raise AttributeError like zope.interface's
- # __provides__ attributes even though they exist. This causes
- # AttributeErrors even though they are listed in dir(cls).
- try:
- value = getattr(cls, key)
- except AttributeError:
- pass
- else:
- if isinstance(value, BaseDescriptor):
- value.instance_init(self)
-
-
+ cls = self.__class__
+ for key in dir(cls):
+ # Some descriptors raise AttributeError like zope.interface's
+ # __provides__ attributes even though they exist. This causes
+ # AttributeErrors even though they are listed in dir(cls).
+ try:
+ value = getattr(cls, key)
+ except AttributeError:
+ pass
+ else:
+ if isinstance(value, BaseDescriptor):
+ value.instance_init(self)
+
+
class HasTraits(six.with_metaclass(MetaHasTraits, HasDescriptors)):
-
+
def setup_instance(self, *args, **kwargs):
- self._trait_values = {}
- self._trait_notifiers = {}
- self._trait_validators = {}
+ self._trait_values = {}
+ self._trait_notifiers = {}
+ self._trait_validators = {}
super(HasTraits, self).setup_instance(*args, **kwargs)
-
+
def __init__(self, *args, **kwargs):
- # Allow trait values to be set using keyword arguments.
- # We need to use setattr for this to trigger validation and
- # notifications.
+ # Allow trait values to be set using keyword arguments.
+ # We need to use setattr for this to trigger validation and
+ # notifications.
super_args = args
super_kwargs = {}
- with self.hold_trait_notifications():
+ with self.hold_trait_notifications():
for key, value in kwargs.items():
if self.has_trait(key):
setattr(self, key, value)
@@ -1017,35 +1017,35 @@ class HasTraits(six.with_metaclass(MetaHasTraits, HasDescriptors)):
DeprecationWarning,
stacklevel=2,
)
-
- def __getstate__(self):
- d = self.__dict__.copy()
- # event handlers stored on an instance are
- # expected to be reinstantiated during a
- # recall of instance_init during __setstate__
- d['_trait_notifiers'] = {}
- d['_trait_validators'] = {}
- return d
-
- def __setstate__(self, state):
- self.__dict__ = state.copy()
-
- # event handlers are reassigned to self
- cls = self.__class__
- for key in dir(cls):
- # Some descriptors raise AttributeError like zope.interface's
- # __provides__ attributes even though they exist. This causes
- # AttributeErrors even though they are listed in dir(cls).
- try:
- value = getattr(cls, key)
- except AttributeError:
- pass
- else:
- if isinstance(value, EventHandler):
- value.instance_init(self)
-
+
+ def __getstate__(self):
+ d = self.__dict__.copy()
+ # event handlers stored on an instance are
+ # expected to be reinstantiated during a
+ # recall of instance_init during __setstate__
+ d['_trait_notifiers'] = {}
+ d['_trait_validators'] = {}
+ return d
+
+ def __setstate__(self, state):
+ self.__dict__ = state.copy()
+
+ # event handlers are reassigned to self
+ cls = self.__class__
+ for key in dir(cls):
+ # Some descriptors raise AttributeError like zope.interface's
+ # __provides__ attributes even though they exist. This causes
+ # AttributeErrors even though they are listed in dir(cls).
+ try:
+ value = getattr(cls, key)
+ except AttributeError:
+ pass
+ else:
+ if isinstance(value, EventHandler):
+ value.instance_init(self)
+
@property
- @contextlib.contextmanager
+ @contextlib.contextmanager
def cross_validation_lock(self):
"""
A contextmanager for running a block with our cross validation lock set
@@ -1065,72 +1065,72 @@ class HasTraits(six.with_metaclass(MetaHasTraits, HasDescriptors)):
self._cross_validation_lock = False
@contextlib.contextmanager
- def hold_trait_notifications(self):
- """Context manager for bundling trait change notifications and cross
- validation.
-
- Use this when doing multiple trait assignments (init, config), to avoid
- race conditions in trait notifiers requesting other trait values.
- All trait notifications will fire after all values have been assigned.
- """
+ def hold_trait_notifications(self):
+ """Context manager for bundling trait change notifications and cross
+ validation.
+
+ Use this when doing multiple trait assignments (init, config), to avoid
+ race conditions in trait notifiers requesting other trait values.
+ All trait notifications will fire after all values have been assigned.
+ """
if self._cross_validation_lock:
- yield
- return
- else:
- cache = {}
- notify_change = self.notify_change
-
- def compress(past_changes, change):
- """Merges the provided change with the last if possible."""
- if past_changes is None:
- return [change]
- else:
+ yield
+ return
+ else:
+ cache = {}
+ notify_change = self.notify_change
+
+ def compress(past_changes, change):
+ """Merges the provided change with the last if possible."""
+ if past_changes is None:
+ return [change]
+ else:
if past_changes[-1]['type'] == 'change' and change.type == 'change':
past_changes[-1]['new'] = change.new
- else:
- # In case of changes other than 'change', append the notification.
- past_changes.append(change)
- return past_changes
-
- def hold(change):
+ else:
+ # In case of changes other than 'change', append the notification.
+ past_changes.append(change)
+ return past_changes
+
+ def hold(change):
name = change.name
- cache[name] = compress(cache.get(name), change)
-
- try:
- # Replace notify_change with `hold`, caching and compressing
- # notifications, disable cross validation and yield.
- self.notify_change = hold
- self._cross_validation_lock = True
- yield
- # Cross validate final values when context is released.
- for name in list(cache.keys()):
- trait = getattr(self.__class__, name)
- value = trait._cross_validate(self, getattr(self, name))
+ cache[name] = compress(cache.get(name), change)
+
+ try:
+ # Replace notify_change with `hold`, caching and compressing
+ # notifications, disable cross validation and yield.
+ self.notify_change = hold
+ self._cross_validation_lock = True
+ yield
+ # Cross validate final values when context is released.
+ for name in list(cache.keys()):
+ trait = getattr(self.__class__, name)
+ value = trait._cross_validate(self, getattr(self, name))
self.set_trait(name, value)
- except TraitError as e:
- # Roll back in case of TraitError during final cross validation.
- self.notify_change = lambda x: None
- for name, changes in cache.items():
- for change in changes[::-1]:
- # TODO: Separate in a rollback function per notification type.
+ except TraitError as e:
+ # Roll back in case of TraitError during final cross validation.
+ self.notify_change = lambda x: None
+ for name, changes in cache.items():
+ for change in changes[::-1]:
+ # TODO: Separate in a rollback function per notification type.
if change.type == 'change':
if change.old is not Undefined:
self.set_trait(name, change.old)
- else:
- self._trait_values.pop(name)
- cache = {}
- raise e
- finally:
- self._cross_validation_lock = False
+ else:
+ self._trait_values.pop(name)
+ cache = {}
+ raise e
+ finally:
+ self._cross_validation_lock = False
# Restore method retrieval from class
del self.notify_change
-
- # trigger delayed notifications
- for changes in cache.values():
- for change in changes:
- self.notify_change(change)
-
- def _notify_trait(self, name, old_value, new_value):
+
+ # trigger delayed notifications
+ for changes in cache.values():
+ for change in changes:
+ self.notify_change(change)
+
+ def _notify_trait(self, name, old_value, new_value):
self.notify_change(Bunch(
name=name,
old=old_value,
@@ -1138,194 +1138,194 @@ class HasTraits(six.with_metaclass(MetaHasTraits, HasDescriptors)):
owner=self,
type='change',
))
-
- def notify_change(self, change):
+
+ def notify_change(self, change):
if not isinstance(change, Bunch):
# cast to bunch if given a dict
change = Bunch(change)
name, type = change.name, change.type
-
- callables = []
- callables.extend(self._trait_notifiers.get(name, {}).get(type, []))
- callables.extend(self._trait_notifiers.get(name, {}).get(All, []))
- callables.extend(self._trait_notifiers.get(All, {}).get(type, []))
- callables.extend(self._trait_notifiers.get(All, {}).get(All, []))
-
- # Now static ones
- magic_name = '_%s_changed' % name
- if hasattr(self, magic_name):
- class_value = getattr(self.__class__, magic_name)
- if not isinstance(class_value, ObserveHandler):
- _deprecated_method(class_value, self.__class__, magic_name,
- "use @observe and @unobserve instead.")
- cb = getattr(self, magic_name)
- # Only append the magic method if it was not manually registered
- if cb not in callables:
- callables.append(_callback_wrapper(cb))
-
- # Call them all now
- # Traits catches and logs errors here. I allow them to raise
- for c in callables:
- # Bound methods have an additional 'self' argument.
-
- if isinstance(c, _CallbackWrapper):
- c = c.__call__
+
+ callables = []
+ callables.extend(self._trait_notifiers.get(name, {}).get(type, []))
+ callables.extend(self._trait_notifiers.get(name, {}).get(All, []))
+ callables.extend(self._trait_notifiers.get(All, {}).get(type, []))
+ callables.extend(self._trait_notifiers.get(All, {}).get(All, []))
+
+ # Now static ones
+ magic_name = '_%s_changed' % name
+ if hasattr(self, magic_name):
+ class_value = getattr(self.__class__, magic_name)
+ if not isinstance(class_value, ObserveHandler):
+ _deprecated_method(class_value, self.__class__, magic_name,
+ "use @observe and @unobserve instead.")
+ cb = getattr(self, magic_name)
+ # Only append the magic method if it was not manually registered
+ if cb not in callables:
+ callables.append(_callback_wrapper(cb))
+
+ # Call them all now
+ # Traits catches and logs errors here. I allow them to raise
+ for c in callables:
+ # Bound methods have an additional 'self' argument.
+
+ if isinstance(c, _CallbackWrapper):
+ c = c.__call__
elif isinstance(c, EventHandler) and c.name is not None:
- c = getattr(self, c.name)
-
- c(change)
-
- def _add_notifiers(self, handler, name, type):
- if name not in self._trait_notifiers:
- nlist = []
- self._trait_notifiers[name] = {type: nlist}
- else:
- if type not in self._trait_notifiers[name]:
- nlist = []
- self._trait_notifiers[name][type] = nlist
- else:
- nlist = self._trait_notifiers[name][type]
- if handler not in nlist:
- nlist.append(handler)
-
- def _remove_notifiers(self, handler, name, type):
- try:
- if handler is None:
- del self._trait_notifiers[name][type]
- else:
- self._trait_notifiers[name][type].remove(handler)
- except KeyError:
- pass
-
- def on_trait_change(self, handler=None, name=None, remove=False):
- """DEPRECATED: Setup a handler to be called when a trait changes.
-
- This is used to setup dynamic notifications of trait changes.
-
- Static handlers can be created by creating methods on a HasTraits
- subclass with the naming convention '_[traitname]_changed'. Thus,
- to create static handler for the trait 'a', create the method
- _a_changed(self, name, old, new) (fewer arguments can be used, see
- below).
-
- If `remove` is True and `handler` is not specified, all change
- handlers for the specified name are uninstalled.
-
- Parameters
- ----------
- handler : callable, None
- A callable that is called when a trait changes. Its
- signature can be handler(), handler(name), handler(name, new),
- handler(name, old, new), or handler(name, old, new, self).
- name : list, str, None
- If None, the handler will apply to all traits. If a list
- of str, handler will apply to all names in the list. If a
- str, the handler will apply just to that name.
- remove : bool
- If False (the default), then install the handler. If True
- then unintall it.
- """
+ c = getattr(self, c.name)
+
+ c(change)
+
+ def _add_notifiers(self, handler, name, type):
+ if name not in self._trait_notifiers:
+ nlist = []
+ self._trait_notifiers[name] = {type: nlist}
+ else:
+ if type not in self._trait_notifiers[name]:
+ nlist = []
+ self._trait_notifiers[name][type] = nlist
+ else:
+ nlist = self._trait_notifiers[name][type]
+ if handler not in nlist:
+ nlist.append(handler)
+
+ def _remove_notifiers(self, handler, name, type):
+ try:
+ if handler is None:
+ del self._trait_notifiers[name][type]
+ else:
+ self._trait_notifiers[name][type].remove(handler)
+ except KeyError:
+ pass
+
+ def on_trait_change(self, handler=None, name=None, remove=False):
+ """DEPRECATED: Setup a handler to be called when a trait changes.
+
+ This is used to setup dynamic notifications of trait changes.
+
+ Static handlers can be created by creating methods on a HasTraits
+ subclass with the naming convention '_[traitname]_changed'. Thus,
+ to create static handler for the trait 'a', create the method
+ _a_changed(self, name, old, new) (fewer arguments can be used, see
+ below).
+
+ If `remove` is True and `handler` is not specified, all change
+ handlers for the specified name are uninstalled.
+
+ Parameters
+ ----------
+ handler : callable, None
+ A callable that is called when a trait changes. Its
+ signature can be handler(), handler(name), handler(name, new),
+ handler(name, old, new), or handler(name, old, new, self).
+ name : list, str, None
+ If None, the handler will apply to all traits. If a list
+ of str, handler will apply to all names in the list. If a
+ str, the handler will apply just to that name.
+ remove : bool
+ If False (the default), then install the handler. If True
+ then unintall it.
+ """
warn("on_trait_change is deprecated in traitlets 4.1: use observe instead",
- DeprecationWarning, stacklevel=2)
- if name is None:
- name = All
- if remove:
- self.unobserve(_callback_wrapper(handler), names=name)
- else:
- self.observe(_callback_wrapper(handler), names=name)
-
- def observe(self, handler, names=All, type='change'):
- """Setup a handler to be called when a trait changes.
-
- This is used to setup dynamic notifications of trait changes.
-
- Parameters
- ----------
- handler : callable
- A callable that is called when a trait changes. Its
+ DeprecationWarning, stacklevel=2)
+ if name is None:
+ name = All
+ if remove:
+ self.unobserve(_callback_wrapper(handler), names=name)
+ else:
+ self.observe(_callback_wrapper(handler), names=name)
+
+ def observe(self, handler, names=All, type='change'):
+ """Setup a handler to be called when a trait changes.
+
+ This is used to setup dynamic notifications of trait changes.
+
+ Parameters
+ ----------
+ handler : callable
+ A callable that is called when a trait changes. Its
signature should be ``handler(change)``, where ``change`` is a
dictionary. The change dictionary at least holds a 'type' key.
- * ``type``: the type of notification.
- Other keys may be passed depending on the value of 'type'. In the
- case where type is 'change', we also have the following keys:
- * ``owner`` : the HasTraits instance
- * ``old`` : the old value of the modified trait attribute
- * ``new`` : the new value of the modified trait attribute
- * ``name`` : the name of the modified trait attribute.
- names : list, str, All
- If names is All, the handler will apply to all traits. If a list
- of str, handler will apply to all names in the list. If a
- str, the handler will apply just to that name.
- type : str, All (default: 'change')
- The type of notification to filter by. If equal to All, then all
- notifications are passed to the observe handler.
- """
- names = parse_notifier_name(names)
- for n in names:
- self._add_notifiers(handler, n, type)
-
- def unobserve(self, handler, names=All, type='change'):
- """Remove a trait change handler.
-
+ * ``type``: the type of notification.
+ Other keys may be passed depending on the value of 'type'. In the
+ case where type is 'change', we also have the following keys:
+ * ``owner`` : the HasTraits instance
+ * ``old`` : the old value of the modified trait attribute
+ * ``new`` : the new value of the modified trait attribute
+ * ``name`` : the name of the modified trait attribute.
+ names : list, str, All
+ If names is All, the handler will apply to all traits. If a list
+ of str, handler will apply to all names in the list. If a
+ str, the handler will apply just to that name.
+ type : str, All (default: 'change')
+ The type of notification to filter by. If equal to All, then all
+ notifications are passed to the observe handler.
+ """
+ names = parse_notifier_name(names)
+ for n in names:
+ self._add_notifiers(handler, n, type)
+
+ def unobserve(self, handler, names=All, type='change'):
+ """Remove a trait change handler.
+
This is used to unregister handlers to trait change notifications.
-
- Parameters
- ----------
- handler : callable
- The callable called when a trait attribute changes.
- names : list, str, All (default: All)
- The names of the traits for which the specified handler should be
- uninstalled. If names is All, the specified handler is uninstalled
- from the list of notifiers corresponding to all changes.
- type : str or All (default: 'change')
- The type of notification to filter by. If All, the specified handler
- is uninstalled from the list of notifiers corresponding to all types.
- """
- names = parse_notifier_name(names)
- for n in names:
- self._remove_notifiers(handler, n, type)
-
- def unobserve_all(self, name=All):
- """Remove trait change handlers of any type for the specified name.
- If name is not specified, removes all trait notifiers."""
- if name is All:
- self._trait_notifiers = {}
- else:
- try:
- del self._trait_notifiers[name]
- except KeyError:
- pass
-
- def _register_validator(self, handler, names):
+
+ Parameters
+ ----------
+ handler : callable
+ The callable called when a trait attribute changes.
+ names : list, str, All (default: All)
+ The names of the traits for which the specified handler should be
+ uninstalled. If names is All, the specified handler is uninstalled
+ from the list of notifiers corresponding to all changes.
+ type : str or All (default: 'change')
+ The type of notification to filter by. If All, the specified handler
+ is uninstalled from the list of notifiers corresponding to all types.
+ """
+ names = parse_notifier_name(names)
+ for n in names:
+ self._remove_notifiers(handler, n, type)
+
+ def unobserve_all(self, name=All):
+ """Remove trait change handlers of any type for the specified name.
+ If name is not specified, removes all trait notifiers."""
+ if name is All:
+ self._trait_notifiers = {}
+ else:
+ try:
+ del self._trait_notifiers[name]
+ except KeyError:
+ pass
+
+ def _register_validator(self, handler, names):
"""Setup a handler to be called when a trait should be cross validated.
-
- This is used to setup dynamic notifications for cross-validation.
-
- If a validator is already registered for any of the provided names, a
+
+ This is used to setup dynamic notifications for cross-validation.
+
+ If a validator is already registered for any of the provided names, a
TraitError is raised and no new validator is registered.
-
- Parameters
- ----------
- handler : callable
- A callable that is called when the given trait is cross-validated.
+
+ Parameters
+ ----------
+ handler : callable
+ A callable that is called when the given trait is cross-validated.
Its signature is handler(proposal), where proposal is a Bunch (dictionary with attribute access)
with the following attributes/keys:
- * ``owner`` : the HasTraits instance
- * ``value`` : the proposed value for the modified trait attribute
- * ``trait`` : the TraitType instance associated with the attribute
- names : List of strings
- The names of the traits that should be cross-validated
- """
- for name in names:
- magic_name = '_%s_validate' % name
- if hasattr(self, magic_name):
- class_value = getattr(self.__class__, magic_name)
- if not isinstance(class_value, ValidateHandler):
- _deprecated_method(class_value, self.__class, magic_name,
- "use @validate decorator instead.")
- for name in names:
- self._trait_validators[name] = handler
-
+ * ``owner`` : the HasTraits instance
+ * ``value`` : the proposed value for the modified trait attribute
+ * ``trait`` : the TraitType instance associated with the attribute
+ names : List of strings
+ The names of the traits that should be cross-validated
+ """
+ for name in names:
+ magic_name = '_%s_validate' % name
+ if hasattr(self, magic_name):
+ class_value = getattr(self.__class__, magic_name)
+ if not isinstance(class_value, ValidateHandler):
+ _deprecated_method(class_value, self.__class, magic_name,
+ "use @validate decorator instead.")
+ for name in names:
+ self._trait_validators[name] = handler
+
def add_traits(self, **traits):
"""Dynamically add trait attributes to the HasTraits instance."""
self.__class__ = type(self.__class__.__name__, (self.__class__,),
@@ -1342,123 +1342,123 @@ class HasTraits(six.with_metaclass(MetaHasTraits, HasDescriptors)):
else:
getattr(cls, name).set(self, value)
- @classmethod
- def class_trait_names(cls, **metadata):
- """Get a list of all the names of this class' traits.
-
- This method is just like the :meth:`trait_names` method,
- but is unbound.
- """
+ @classmethod
+ def class_trait_names(cls, **metadata):
+ """Get a list of all the names of this class' traits.
+
+ This method is just like the :meth:`trait_names` method,
+ but is unbound.
+ """
return list(cls.class_traits(**metadata))
-
- @classmethod
- def class_traits(cls, **metadata):
- """Get a ``dict`` of all the traits of this class. The dictionary
- is keyed on the name and the values are the TraitType objects.
-
- This method is just like the :meth:`traits` method, but is unbound.
-
- The TraitTypes returned don't know anything about the values
- that the various HasTrait's instances are holding.
-
- The metadata kwargs allow functions to be passed in which
- filter traits based on metadata values. The functions should
- take a single value as an argument and return a boolean. If
- any function returns False, then the trait is not included in
- the output. If a metadata key doesn't exist, None will be passed
- to the function.
- """
- traits = dict([memb for memb in getmembers(cls) if
- isinstance(memb[1], TraitType)])
-
- if len(metadata) == 0:
- return traits
-
- result = {}
- for name, trait in traits.items():
- for meta_name, meta_eval in metadata.items():
- if type(meta_eval) is not types.FunctionType:
- meta_eval = _SimpleTest(meta_eval)
- if not meta_eval(trait.metadata.get(meta_name, None)):
- break
- else:
- result[name] = trait
-
- return result
-
- @classmethod
- def class_own_traits(cls, **metadata):
- """Get a dict of all the traitlets defined on this class, not a parent.
-
- Works like `class_traits`, except for excluding traits from parents.
- """
- sup = super(cls, cls)
- return {n: t for (n, t) in cls.class_traits(**metadata).items()
- if getattr(sup, n, None) is not t}
-
- def has_trait(self, name):
- """Returns True if the object has a trait with the specified name."""
- return isinstance(getattr(self.__class__, name, None), TraitType)
-
- def trait_names(self, **metadata):
- """Get a list of all the names of this class' traits."""
+
+ @classmethod
+ def class_traits(cls, **metadata):
+ """Get a ``dict`` of all the traits of this class. The dictionary
+ is keyed on the name and the values are the TraitType objects.
+
+ This method is just like the :meth:`traits` method, but is unbound.
+
+ The TraitTypes returned don't know anything about the values
+ that the various HasTrait's instances are holding.
+
+ The metadata kwargs allow functions to be passed in which
+ filter traits based on metadata values. The functions should
+ take a single value as an argument and return a boolean. If
+ any function returns False, then the trait is not included in
+ the output. If a metadata key doesn't exist, None will be passed
+ to the function.
+ """
+ traits = dict([memb for memb in getmembers(cls) if
+ isinstance(memb[1], TraitType)])
+
+ if len(metadata) == 0:
+ return traits
+
+ result = {}
+ for name, trait in traits.items():
+ for meta_name, meta_eval in metadata.items():
+ if type(meta_eval) is not types.FunctionType:
+ meta_eval = _SimpleTest(meta_eval)
+ if not meta_eval(trait.metadata.get(meta_name, None)):
+ break
+ else:
+ result[name] = trait
+
+ return result
+
+ @classmethod
+ def class_own_traits(cls, **metadata):
+ """Get a dict of all the traitlets defined on this class, not a parent.
+
+ Works like `class_traits`, except for excluding traits from parents.
+ """
+ sup = super(cls, cls)
+ return {n: t for (n, t) in cls.class_traits(**metadata).items()
+ if getattr(sup, n, None) is not t}
+
+ def has_trait(self, name):
+ """Returns True if the object has a trait with the specified name."""
+ return isinstance(getattr(self.__class__, name, None), TraitType)
+
+ def trait_names(self, **metadata):
+ """Get a list of all the names of this class' traits."""
return list(self.traits(**metadata))
-
- def traits(self, **metadata):
- """Get a ``dict`` of all the traits of this class. The dictionary
- is keyed on the name and the values are the TraitType objects.
-
- The TraitTypes returned don't know anything about the values
- that the various HasTrait's instances are holding.
-
- The metadata kwargs allow functions to be passed in which
- filter traits based on metadata values. The functions should
- take a single value as an argument and return a boolean. If
- any function returns False, then the trait is not included in
- the output. If a metadata key doesn't exist, None will be passed
- to the function.
- """
- traits = dict([memb for memb in getmembers(self.__class__) if
- isinstance(memb[1], TraitType)])
-
- if len(metadata) == 0:
- return traits
-
- result = {}
- for name, trait in traits.items():
- for meta_name, meta_eval in metadata.items():
- if type(meta_eval) is not types.FunctionType:
- meta_eval = _SimpleTest(meta_eval)
- if not meta_eval(trait.metadata.get(meta_name, None)):
- break
- else:
- result[name] = trait
-
- return result
-
- def trait_metadata(self, traitname, key, default=None):
- """Get metadata values for trait by key."""
- try:
- trait = getattr(self.__class__, traitname)
- except AttributeError:
- raise TraitError("Class %s does not have a trait named %s" %
- (self.__class__.__name__, traitname))
+
+ def traits(self, **metadata):
+ """Get a ``dict`` of all the traits of this class. The dictionary
+ is keyed on the name and the values are the TraitType objects.
+
+ The TraitTypes returned don't know anything about the values
+ that the various HasTrait's instances are holding.
+
+ The metadata kwargs allow functions to be passed in which
+ filter traits based on metadata values. The functions should
+ take a single value as an argument and return a boolean. If
+ any function returns False, then the trait is not included in
+ the output. If a metadata key doesn't exist, None will be passed
+ to the function.
+ """
+ traits = dict([memb for memb in getmembers(self.__class__) if
+ isinstance(memb[1], TraitType)])
+
+ if len(metadata) == 0:
+ return traits
+
+ result = {}
+ for name, trait in traits.items():
+ for meta_name, meta_eval in metadata.items():
+ if type(meta_eval) is not types.FunctionType:
+ meta_eval = _SimpleTest(meta_eval)
+ if not meta_eval(trait.metadata.get(meta_name, None)):
+ break
+ else:
+ result[name] = trait
+
+ return result
+
+ def trait_metadata(self, traitname, key, default=None):
+ """Get metadata values for trait by key."""
+ try:
+ trait = getattr(self.__class__, traitname)
+ except AttributeError:
+ raise TraitError("Class %s does not have a trait named %s" %
+ (self.__class__.__name__, traitname))
metadata_name = '_' + traitname + '_metadata'
if hasattr(self, metadata_name) and key in getattr(self, metadata_name):
return getattr(self, metadata_name).get(key, default)
- else:
- return trait.metadata.get(key, default)
-
+ else:
+ return trait.metadata.get(key, default)
+
@classmethod
def class_own_trait_events(cls, name):
"""Get a dict of all event handlers defined on this class, not a parent.
-
+
Works like ``event_handlers``, except for excluding traits from parents.
"""
sup = super(cls, cls)
return {n: e for (n, e) in cls.events(name).items()
if getattr(sup, n, None) is not e}
-
+
@classmethod
def trait_events(cls, name=None):
"""Get a ``dict`` of all the event handlers of this class.
@@ -1485,329 +1485,329 @@ class HasTraits(six.with_metaclass(MetaHasTraits, HasDescriptors)):
events[k] = v
return events
-#-----------------------------------------------------------------------------
-# Actual TraitTypes implementations/subclasses
-#-----------------------------------------------------------------------------
-
-#-----------------------------------------------------------------------------
-# TraitTypes subclasses for handling classes and instances of classes
-#-----------------------------------------------------------------------------
-
-
-class ClassBasedTraitType(TraitType):
- """
- A trait with error reporting and string -> type resolution for Type,
- Instance and This.
- """
-
- def _resolve_string(self, string):
- """
- Resolve a string supplied for a type into an actual object.
- """
- return import_item(string)
-
- def error(self, obj, value):
- kind = type(value)
+#-----------------------------------------------------------------------------
+# Actual TraitTypes implementations/subclasses
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# TraitTypes subclasses for handling classes and instances of classes
+#-----------------------------------------------------------------------------
+
+
+class ClassBasedTraitType(TraitType):
+ """
+ A trait with error reporting and string -> type resolution for Type,
+ Instance and This.
+ """
+
+ def _resolve_string(self, string):
+ """
+ Resolve a string supplied for a type into an actual object.
+ """
+ return import_item(string)
+
+ def error(self, obj, value):
+ kind = type(value)
if six.PY2 and kind is InstanceType:
- msg = 'class %s' % value.__class__.__name__
- else:
- msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
-
- if obj is not None:
- e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
- % (self.name, class_of(obj),
- self.info(), msg)
- else:
- e = "The '%s' trait must be %s, but a value of %r was specified." \
- % (self.name, self.info(), msg)
-
- raise TraitError(e)
-
-
-class Type(ClassBasedTraitType):
- """A trait whose value must be a subclass of a specified class."""
-
+ msg = 'class %s' % value.__class__.__name__
+ else:
+ msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
+
+ if obj is not None:
+ e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
+ % (self.name, class_of(obj),
+ self.info(), msg)
+ else:
+ e = "The '%s' trait must be %s, but a value of %r was specified." \
+ % (self.name, self.info(), msg)
+
+ raise TraitError(e)
+
+
+class Type(ClassBasedTraitType):
+ """A trait whose value must be a subclass of a specified class."""
+
def __init__ (self, default_value=Undefined, klass=None, **kwargs):
- """Construct a Type trait
-
- A Type trait specifies that its values must be subclasses of
- a particular class.
-
- If only ``default_value`` is given, it is used for the ``klass`` as
- well. If neither are given, both default to ``object``.
-
- Parameters
- ----------
- default_value : class, str or None
- The default value must be a subclass of klass. If an str,
- the str must be a fully specified class name, like 'foo.bar.Bah'.
- The string is resolved into real class, when the parent
- :class:`HasTraits` class is instantiated.
- klass : class, str [ default object ]
- Values of this trait must be a subclass of klass. The klass
- may be specified in a string like: 'foo.bar.MyClass'.
- The string is resolved into real class, when the parent
- :class:`HasTraits` class is instantiated.
- allow_none : bool [ default False ]
- Indicates whether None is allowed as an assignable value.
- """
- if default_value is Undefined:
- new_default_value = object if (klass is None) else klass
- else:
- new_default_value = default_value
-
- if klass is None:
- if (default_value is None) or (default_value is Undefined):
- klass = object
- else:
- klass = default_value
-
+ """Construct a Type trait
+
+ A Type trait specifies that its values must be subclasses of
+ a particular class.
+
+ If only ``default_value`` is given, it is used for the ``klass`` as
+ well. If neither are given, both default to ``object``.
+
+ Parameters
+ ----------
+ default_value : class, str or None
+ The default value must be a subclass of klass. If an str,
+ the str must be a fully specified class name, like 'foo.bar.Bah'.
+ The string is resolved into real class, when the parent
+ :class:`HasTraits` class is instantiated.
+ klass : class, str [ default object ]
+ Values of this trait must be a subclass of klass. The klass
+ may be specified in a string like: 'foo.bar.MyClass'.
+ The string is resolved into real class, when the parent
+ :class:`HasTraits` class is instantiated.
+ allow_none : bool [ default False ]
+ Indicates whether None is allowed as an assignable value.
+ """
+ if default_value is Undefined:
+ new_default_value = object if (klass is None) else klass
+ else:
+ new_default_value = default_value
+
+ if klass is None:
+ if (default_value is None) or (default_value is Undefined):
+ klass = object
+ else:
+ klass = default_value
+
if not (inspect.isclass(klass) or isinstance(klass, six.string_types)):
- raise TraitError("A Type trait must specify a class.")
-
- self.klass = klass
-
+ raise TraitError("A Type trait must specify a class.")
+
+ self.klass = klass
+
super(Type, self).__init__(new_default_value, **kwargs)
-
- def validate(self, obj, value):
- """Validates that the value is a valid object instance."""
+
+ def validate(self, obj, value):
+ """Validates that the value is a valid object instance."""
if isinstance(value, six.string_types):
- try:
- value = self._resolve_string(value)
- except ImportError:
- raise TraitError("The '%s' trait of %s instance must be a type, but "
- "%r could not be imported" % (self.name, obj, value))
- try:
- if issubclass(value, self.klass):
- return value
- except:
- pass
-
- self.error(obj, value)
-
- def info(self):
- """ Returns a description of the trait."""
+ try:
+ value = self._resolve_string(value)
+ except ImportError:
+ raise TraitError("The '%s' trait of %s instance must be a type, but "
+ "%r could not be imported" % (self.name, obj, value))
+ try:
+ if issubclass(value, self.klass):
+ return value
+ except:
+ pass
+
+ self.error(obj, value)
+
+ def info(self):
+ """ Returns a description of the trait."""
if isinstance(self.klass, six.string_types):
- klass = self.klass
- else:
+ klass = self.klass
+ else:
klass = self.klass.__module__ + '.' + self.klass.__name__
- result = "a subclass of '%s'" % klass
- if self.allow_none:
- return result + ' or None'
- return result
-
- def instance_init(self, obj):
- self._resolve_classes()
- super(Type, self).instance_init(obj)
-
- def _resolve_classes(self):
+ result = "a subclass of '%s'" % klass
+ if self.allow_none:
+ return result + ' or None'
+ return result
+
+ def instance_init(self, obj):
+ self._resolve_classes()
+ super(Type, self).instance_init(obj)
+
+ def _resolve_classes(self):
if isinstance(self.klass, six.string_types):
- self.klass = self._resolve_string(self.klass)
+ self.klass = self._resolve_string(self.klass)
if isinstance(self.default_value, six.string_types):
- self.default_value = self._resolve_string(self.default_value)
-
- def default_value_repr(self):
- value = self.default_value
+ self.default_value = self._resolve_string(self.default_value)
+
+ def default_value_repr(self):
+ value = self.default_value
if isinstance(value, six.string_types):
- return repr(value)
- else:
- return repr('{}.{}'.format(value.__module__, value.__name__))
-
-
-class Instance(ClassBasedTraitType):
- """A trait whose value must be an instance of a specified class.
-
- The value can also be an instance of a subclass of the specified class.
-
- Subclasses can declare default classes by overriding the klass attribute
- """
-
- klass = None
-
+ return repr(value)
+ else:
+ return repr('{}.{}'.format(value.__module__, value.__name__))
+
+
+class Instance(ClassBasedTraitType):
+ """A trait whose value must be an instance of a specified class.
+
+ The value can also be an instance of a subclass of the specified class.
+
+ Subclasses can declare default classes by overriding the klass attribute
+ """
+
+ klass = None
+
def __init__(self, klass=None, args=None, kw=None, **kwargs):
- """Construct an Instance trait.
-
- This trait allows values that are instances of a particular
- class or its subclasses. Our implementation is quite different
- from that of enthough.traits as we don't allow instances to be used
- for klass and we handle the ``args`` and ``kw`` arguments differently.
-
- Parameters
- ----------
- klass : class, str
- The class that forms the basis for the trait. Class names
- can also be specified as strings, like 'foo.bar.Bar'.
- args : tuple
- Positional arguments for generating the default value.
- kw : dict
- Keyword arguments for generating the default value.
- allow_none : bool [ default False ]
- Indicates whether None is allowed as a value.
-
- Notes
- -----
- If both ``args`` and ``kw`` are None, then the default value is None.
- If ``args`` is a tuple and ``kw`` is a dict, then the default is
- created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is
- None, the None is replaced by ``()`` or ``{}``, respectively.
- """
- if klass is None:
- klass = self.klass
+ """Construct an Instance trait.
+
+ This trait allows values that are instances of a particular
+ class or its subclasses. Our implementation is quite different
+ from that of enthough.traits as we don't allow instances to be used
+ for klass and we handle the ``args`` and ``kw`` arguments differently.
+
+ Parameters
+ ----------
+ klass : class, str
+ The class that forms the basis for the trait. Class names
+ can also be specified as strings, like 'foo.bar.Bar'.
+ args : tuple
+ Positional arguments for generating the default value.
+ kw : dict
+ Keyword arguments for generating the default value.
+ allow_none : bool [ default False ]
+ Indicates whether None is allowed as a value.
+
+ Notes
+ -----
+ If both ``args`` and ``kw`` are None, then the default value is None.
+ If ``args`` is a tuple and ``kw`` is a dict, then the default is
+ created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is
+ None, the None is replaced by ``()`` or ``{}``, respectively.
+ """
+ if klass is None:
+ klass = self.klass
if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, six.string_types)):
- self.klass = klass
- else:
- raise TraitError('The klass attribute must be a class'
- ' not: %r' % klass)
-
- if (kw is not None) and not isinstance(kw, dict):
- raise TraitError("The 'kw' argument must be a dict or None.")
- if (args is not None) and not isinstance(args, tuple):
- raise TraitError("The 'args' argument must be a tuple or None.")
-
- self.default_args = args
- self.default_kwargs = kw
-
+ self.klass = klass
+ else:
+ raise TraitError('The klass attribute must be a class'
+ ' not: %r' % klass)
+
+ if (kw is not None) and not isinstance(kw, dict):
+ raise TraitError("The 'kw' argument must be a dict or None.")
+ if (args is not None) and not isinstance(args, tuple):
+ raise TraitError("The 'args' argument must be a tuple or None.")
+
+ self.default_args = args
+ self.default_kwargs = kw
+
super(Instance, self).__init__(**kwargs)
-
- def validate(self, obj, value):
- if isinstance(value, self.klass):
- return value
- else:
- self.error(obj, value)
-
- def info(self):
+
+ def validate(self, obj, value):
+ if isinstance(value, self.klass):
+ return value
+ else:
+ self.error(obj, value)
+
+ def info(self):
if isinstance(self.klass, six.string_types):
- klass = self.klass
- else:
- klass = self.klass.__name__
- result = class_of(klass)
- if self.allow_none:
- return result + ' or None'
-
- return result
-
- def instance_init(self, obj):
- self._resolve_classes()
- super(Instance, self).instance_init(obj)
-
- def _resolve_classes(self):
+ klass = self.klass
+ else:
+ klass = self.klass.__name__
+ result = class_of(klass)
+ if self.allow_none:
+ return result + ' or None'
+
+ return result
+
+ def instance_init(self, obj):
+ self._resolve_classes()
+ super(Instance, self).instance_init(obj)
+
+ def _resolve_classes(self):
if isinstance(self.klass, six.string_types):
- self.klass = self._resolve_string(self.klass)
-
- def make_dynamic_default(self):
- if (self.default_args is None) and (self.default_kwargs is None):
- return None
- return self.klass(*(self.default_args or ()),
- **(self.default_kwargs or {}))
-
- def default_value_repr(self):
- return repr(self.make_dynamic_default())
-
-
-class ForwardDeclaredMixin(object):
- """
- Mixin for forward-declared versions of Instance and Type.
- """
- def _resolve_string(self, string):
- """
- Find the specified class name by looking for it in the module in which
- our this_class attribute was defined.
- """
- modname = self.this_class.__module__
- return import_item('.'.join([modname, string]))
-
-
-class ForwardDeclaredType(ForwardDeclaredMixin, Type):
- """
- Forward-declared version of Type.
- """
- pass
-
-
-class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
- """
- Forward-declared version of Instance.
- """
- pass
-
-
-class This(ClassBasedTraitType):
- """A trait for instances of the class containing this trait.
-
- Because how how and when class bodies are executed, the ``This``
- trait can only have a default value of None. This, and because we
- always validate default values, ``allow_none`` is *always* true.
- """
-
- info_text = 'an instance of the same type as the receiver or None'
-
+ self.klass = self._resolve_string(self.klass)
+
+ def make_dynamic_default(self):
+ if (self.default_args is None) and (self.default_kwargs is None):
+ return None
+ return self.klass(*(self.default_args or ()),
+ **(self.default_kwargs or {}))
+
+ def default_value_repr(self):
+ return repr(self.make_dynamic_default())
+
+
+class ForwardDeclaredMixin(object):
+ """
+ Mixin for forward-declared versions of Instance and Type.
+ """
+ def _resolve_string(self, string):
+ """
+ Find the specified class name by looking for it in the module in which
+ our this_class attribute was defined.
+ """
+ modname = self.this_class.__module__
+ return import_item('.'.join([modname, string]))
+
+
+class ForwardDeclaredType(ForwardDeclaredMixin, Type):
+ """
+ Forward-declared version of Type.
+ """
+ pass
+
+
+class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
+ """
+ Forward-declared version of Instance.
+ """
+ pass
+
+
+class This(ClassBasedTraitType):
+ """A trait for instances of the class containing this trait.
+
+ Because how how and when class bodies are executed, the ``This``
+ trait can only have a default value of None. This, and because we
+ always validate default values, ``allow_none`` is *always* true.
+ """
+
+ info_text = 'an instance of the same type as the receiver or None'
+
def __init__(self, **kwargs):
super(This, self).__init__(None, **kwargs)
-
- def validate(self, obj, value):
- # What if value is a superclass of obj.__class__? This is
- # complicated if it was the superclass that defined the This
- # trait.
- if isinstance(value, self.this_class) or (value is None):
- return value
- else:
- self.error(obj, value)
-
-
-class Union(TraitType):
- """A trait type representing a Union type."""
-
+
+ def validate(self, obj, value):
+ # What if value is a superclass of obj.__class__? This is
+ # complicated if it was the superclass that defined the This
+ # trait.
+ if isinstance(value, self.this_class) or (value is None):
+ return value
+ else:
+ self.error(obj, value)
+
+
+class Union(TraitType):
+ """A trait type representing a Union type."""
+
def __init__(self, trait_types, **kwargs):
- """Construct a Union trait.
-
- This trait allows values that are allowed by at least one of the
- specified trait types. A Union traitlet cannot have metadata on
- its own, besides the metadata of the listed types.
-
- Parameters
- ----------
- trait_types: sequence
- The list of trait types of length at least 1.
-
- Notes
- -----
- Union([Float(), Bool(), Int()]) attempts to validate the provided values
- with the validation function of Float, then Bool, and finally Int.
- """
- self.trait_types = trait_types
+ """Construct a Union trait.
+
+ This trait allows values that are allowed by at least one of the
+ specified trait types. A Union traitlet cannot have metadata on
+ its own, besides the metadata of the listed types.
+
+ Parameters
+ ----------
+ trait_types: sequence
+ The list of trait types of length at least 1.
+
+ Notes
+ -----
+ Union([Float(), Bool(), Int()]) attempts to validate the provided values
+ with the validation function of Float, then Bool, and finally Int.
+ """
+ self.trait_types = trait_types
self.info_text = " or ".join([tt.info() for tt in self.trait_types])
super(Union, self).__init__(**kwargs)
-
- def class_init(self, cls, name):
- for trait_type in self.trait_types:
- trait_type.class_init(cls, None)
- super(Union, self).class_init(cls, name)
-
- def instance_init(self, obj):
- for trait_type in self.trait_types:
- trait_type.instance_init(obj)
- super(Union, self).instance_init(obj)
-
- def validate(self, obj, value):
+
+ def class_init(self, cls, name):
+ for trait_type in self.trait_types:
+ trait_type.class_init(cls, None)
+ super(Union, self).class_init(cls, name)
+
+ def instance_init(self, obj):
+ for trait_type in self.trait_types:
+ trait_type.instance_init(obj)
+ super(Union, self).instance_init(obj)
+
+ def validate(self, obj, value):
with obj.cross_validation_lock:
- for trait_type in self.trait_types:
- try:
- v = trait_type._validate(obj, value)
+ for trait_type in self.trait_types:
+ try:
+ v = trait_type._validate(obj, value)
# In the case of an element trait, the name is None
if self.name is not None:
setattr(obj, '_' + self.name + '_metadata', trait_type.metadata)
- return v
- except TraitError:
- continue
- self.error(obj, value)
-
- def __or__(self, other):
- if isinstance(other, Union):
- return Union(self.trait_types + other.trait_types)
- else:
- return Union(self.trait_types + [other])
-
+ return v
+ except TraitError:
+ continue
+ self.error(obj, value)
+
+ def __or__(self, other):
+ if isinstance(other, Union):
+ return Union(self.trait_types + other.trait_types)
+ else:
+ return Union(self.trait_types + [other])
+
def make_dynamic_default(self):
if self.default_value is not Undefined:
return self.default_value
@@ -1818,17 +1818,17 @@ class Union(TraitType):
return trait_type.make_dynamic_default()
-#-----------------------------------------------------------------------------
-# Basic TraitTypes implementations/subclasses
-#-----------------------------------------------------------------------------
-
-
-class Any(TraitType):
- """A trait which allows any value."""
- default_value = None
- info_text = 'any value'
-
-
+#-----------------------------------------------------------------------------
+# Basic TraitTypes implementations/subclasses
+#-----------------------------------------------------------------------------
+
+
+class Any(TraitType):
+ """A trait which allows any value."""
+ default_value = None
+ info_text = 'any value'
+
+
def _validate_bounds(trait, obj, value):
"""
Validate that a number to be applied to a trait is between bounds.
@@ -1853,42 +1853,42 @@ def _validate_bounds(trait, obj, value):
return value
-class Int(TraitType):
- """An int trait."""
-
- default_value = 0
- info_text = 'an int'
-
+class Int(TraitType):
+ """An int trait."""
+
+ default_value = 0
+ info_text = 'an int'
+
def __init__(self, default_value=Undefined, allow_none=False, **kwargs):
- self.min = kwargs.pop('min', None)
- self.max = kwargs.pop('max', None)
- super(Int, self).__init__(default_value=default_value,
- allow_none=allow_none, **kwargs)
-
- def validate(self, obj, value):
- if not isinstance(value, int):
- self.error(obj, value)
+ self.min = kwargs.pop('min', None)
+ self.max = kwargs.pop('max', None)
+ super(Int, self).__init__(default_value=default_value,
+ allow_none=allow_none, **kwargs)
+
+ def validate(self, obj, value):
+ if not isinstance(value, int):
+ self.error(obj, value)
return _validate_bounds(self, obj, value)
-
-
-class CInt(Int):
- """A casting version of the int trait."""
-
- def validate(self, obj, value):
- try:
+
+
+class CInt(Int):
+ """A casting version of the int trait."""
+
+ def validate(self, obj, value):
+ try:
value = int(value)
- except:
- self.error(obj, value)
+ except:
+ self.error(obj, value)
return _validate_bounds(self, obj, value)
-
+
if six.PY2:
- class Long(TraitType):
- """A long integer trait."""
-
- default_value = 0
- info_text = 'a long'
-
+ class Long(TraitType):
+ """A long integer trait."""
+
+ default_value = 0
+ info_text = 'a long'
+
def __init__(self, default_value=Undefined, allow_none=False, **kwargs):
self.min = kwargs.pop('min', None)
self.max = kwargs.pop('max', None)
@@ -1897,36 +1897,36 @@ if six.PY2:
allow_none=allow_none, **kwargs)
def _validate_long(self, obj, value):
- if isinstance(value, long):
- return value
- if isinstance(value, int):
- return long(value)
- self.error(obj, value)
-
+ if isinstance(value, long):
+ return value
+ if isinstance(value, int):
+ return long(value)
+ self.error(obj, value)
+
def validate(self, obj, value):
value = self._validate_long(obj, value)
return _validate_bounds(self, obj, value)
+
-
- class CLong(Long):
- """A casting version of the long integer trait."""
-
- def validate(self, obj, value):
- try:
+ class CLong(Long):
+ """A casting version of the long integer trait."""
+
+ def validate(self, obj, value):
+ try:
value = long(value)
- except:
- self.error(obj, value)
+ except:
+ self.error(obj, value)
return _validate_bounds(self, obj, value)
-
-
- class Integer(TraitType):
- """An integer trait.
-
- Longs that are unnecessary (<= sys.maxint) are cast to ints."""
-
- default_value = 0
- info_text = 'an integer'
-
+
+
+ class Integer(TraitType):
+ """An integer trait.
+
+ Longs that are unnecessary (<= sys.maxint) are cast to ints."""
+
+ default_value = 0
+ info_text = 'an integer'
+
def __init__(self, default_value=Undefined, allow_none=False, **kwargs):
self.min = kwargs.pop('min', None)
self.max = kwargs.pop('max', None)
@@ -1935,674 +1935,674 @@ if six.PY2:
allow_none=allow_none, **kwargs)
def _validate_int(self, obj, value):
- if isinstance(value, int):
- return value
- if isinstance(value, long):
- # downcast longs that fit in int:
- # note that int(n > sys.maxint) returns a long, so
- # we don't need a condition on this cast
- return int(value)
- if sys.platform == "cli":
- from System import Int64
- if isinstance(value, Int64):
- return int(value)
- self.error(obj, value)
-
+ if isinstance(value, int):
+ return value
+ if isinstance(value, long):
+ # downcast longs that fit in int:
+ # note that int(n > sys.maxint) returns a long, so
+ # we don't need a condition on this cast
+ return int(value)
+ if sys.platform == "cli":
+ from System import Int64
+ if isinstance(value, Int64):
+ return int(value)
+ self.error(obj, value)
+
def validate(self, obj, value):
value = self._validate_int(obj, value)
return _validate_bounds(self, obj, value)
-
+
else:
Long, CLong = Int, CInt
Integer = Int
-class Float(TraitType):
- """A float trait."""
-
- default_value = 0.0
- info_text = 'a float'
-
+class Float(TraitType):
+ """A float trait."""
+
+ default_value = 0.0
+ info_text = 'a float'
+
def __init__(self, default_value=Undefined, allow_none=False, **kwargs):
- self.min = kwargs.pop('min', -float('inf'))
- self.max = kwargs.pop('max', float('inf'))
+ self.min = kwargs.pop('min', -float('inf'))
+ self.max = kwargs.pop('max', float('inf'))
super(Float, self).__init__(default_value=default_value,
- allow_none=allow_none, **kwargs)
-
- def validate(self, obj, value):
- if isinstance(value, int):
- value = float(value)
- if not isinstance(value, float):
- self.error(obj, value)
+ allow_none=allow_none, **kwargs)
+
+ def validate(self, obj, value):
+ if isinstance(value, int):
+ value = float(value)
+ if not isinstance(value, float):
+ self.error(obj, value)
return _validate_bounds(self, obj, value)
-
-
-class CFloat(Float):
- """A casting version of the float trait."""
-
- def validate(self, obj, value):
- try:
+
+
+class CFloat(Float):
+ """A casting version of the float trait."""
+
+ def validate(self, obj, value):
+ try:
value = float(value)
- except:
- self.error(obj, value)
+ except:
+ self.error(obj, value)
return _validate_bounds(self, obj, value)
-
-
-class Complex(TraitType):
- """A trait for complex numbers."""
-
- default_value = 0.0 + 0.0j
- info_text = 'a complex number'
-
- def validate(self, obj, value):
- if isinstance(value, complex):
- return value
- if isinstance(value, (float, int)):
- return complex(value)
- self.error(obj, value)
-
-
-class CComplex(Complex):
- """A casting version of the complex number trait."""
-
- def validate (self, obj, value):
- try:
- return complex(value)
- except:
- self.error(obj, value)
-
-# We should always be explicit about whether we're using bytes or unicode, both
-# for Python 3 conversion and for reliable unicode behaviour on Python 2. So
-# we don't have a Str type.
-class Bytes(TraitType):
- """A trait for byte strings."""
-
- default_value = b''
- info_text = 'a bytes object'
-
- def validate(self, obj, value):
- if isinstance(value, bytes):
- return value
- self.error(obj, value)
-
-
-class CBytes(Bytes):
- """A casting version of the byte string trait."""
-
- def validate(self, obj, value):
- try:
- return bytes(value)
- except:
- self.error(obj, value)
-
-
-class Unicode(TraitType):
- """A trait for unicode strings."""
-
- default_value = u''
- info_text = 'a unicode string'
-
- def validate(self, obj, value):
+
+
+class Complex(TraitType):
+ """A trait for complex numbers."""
+
+ default_value = 0.0 + 0.0j
+ info_text = 'a complex number'
+
+ def validate(self, obj, value):
+ if isinstance(value, complex):
+ return value
+ if isinstance(value, (float, int)):
+ return complex(value)
+ self.error(obj, value)
+
+
+class CComplex(Complex):
+ """A casting version of the complex number trait."""
+
+ def validate (self, obj, value):
+ try:
+ return complex(value)
+ except:
+ self.error(obj, value)
+
+# We should always be explicit about whether we're using bytes or unicode, both
+# for Python 3 conversion and for reliable unicode behaviour on Python 2. So
+# we don't have a Str type.
+class Bytes(TraitType):
+ """A trait for byte strings."""
+
+ default_value = b''
+ info_text = 'a bytes object'
+
+ def validate(self, obj, value):
+ if isinstance(value, bytes):
+ return value
+ self.error(obj, value)
+
+
+class CBytes(Bytes):
+ """A casting version of the byte string trait."""
+
+ def validate(self, obj, value):
+ try:
+ return bytes(value)
+ except:
+ self.error(obj, value)
+
+
+class Unicode(TraitType):
+ """A trait for unicode strings."""
+
+ default_value = u''
+ info_text = 'a unicode string'
+
+ def validate(self, obj, value):
if isinstance(value, six.text_type):
- return value
- if isinstance(value, bytes):
- try:
- return value.decode('ascii', 'strict')
- except UnicodeDecodeError:
- msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
- raise TraitError(msg.format(value, self.name, class_of(obj)))
- self.error(obj, value)
-
-
-class CUnicode(Unicode):
- """A casting version of the unicode trait."""
-
- def validate(self, obj, value):
- try:
+ return value
+ if isinstance(value, bytes):
+ try:
+ return value.decode('ascii', 'strict')
+ except UnicodeDecodeError:
+ msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
+ raise TraitError(msg.format(value, self.name, class_of(obj)))
+ self.error(obj, value)
+
+
+class CUnicode(Unicode):
+ """A casting version of the unicode trait."""
+
+ def validate(self, obj, value):
+ try:
return six.text_type(value)
- except:
- self.error(obj, value)
-
-
-class ObjectName(TraitType):
- """A string holding a valid object name in this version of Python.
-
- This does not check that the name exists in any scope."""
- info_text = "a valid object identifier in Python"
-
+ except:
+ self.error(obj, value)
+
+
+class ObjectName(TraitType):
+ """A string holding a valid object name in this version of Python.
+
+ This does not check that the name exists in any scope."""
+ info_text = "a valid object identifier in Python"
+
if six.PY2:
- # Python 2:
- def coerce_str(self, obj, value):
- "In Python 2, coerce ascii-only unicode to str"
- if isinstance(value, unicode):
- try:
- return str(value)
- except UnicodeEncodeError:
- self.error(obj, value)
- return value
+ # Python 2:
+ def coerce_str(self, obj, value):
+ "In Python 2, coerce ascii-only unicode to str"
+ if isinstance(value, unicode):
+ try:
+ return str(value)
+ except UnicodeEncodeError:
+ self.error(obj, value)
+ return value
else:
coerce_str = staticmethod(lambda _,s: s)
-
- def validate(self, obj, value):
- value = self.coerce_str(obj, value)
-
+
+ def validate(self, obj, value):
+ value = self.coerce_str(obj, value)
+
if isinstance(value, six.string_types) and isidentifier(value):
- return value
- self.error(obj, value)
-
-class DottedObjectName(ObjectName):
- """A string holding a valid dotted object name in Python, such as A.b3._c"""
- def validate(self, obj, value):
- value = self.coerce_str(obj, value)
-
+ return value
+ self.error(obj, value)
+
+class DottedObjectName(ObjectName):
+ """A string holding a valid dotted object name in Python, such as A.b3._c"""
+ def validate(self, obj, value):
+ value = self.coerce_str(obj, value)
+
if isinstance(value, six.string_types) and all(isidentifier(a)
for a in value.split('.')):
- return value
- self.error(obj, value)
-
-
-class Bool(TraitType):
- """A boolean (True, False) trait."""
-
- default_value = False
- info_text = 'a boolean'
-
- def validate(self, obj, value):
- if isinstance(value, bool):
- return value
- self.error(obj, value)
-
-
-class CBool(Bool):
- """A casting version of the boolean trait."""
-
- def validate(self, obj, value):
- try:
- return bool(value)
- except:
- self.error(obj, value)
-
-
-class Enum(TraitType):
- """An enum whose value must be in a given sequence."""
-
+ return value
+ self.error(obj, value)
+
+
+class Bool(TraitType):
+ """A boolean (True, False) trait."""
+
+ default_value = False
+ info_text = 'a boolean'
+
+ def validate(self, obj, value):
+ if isinstance(value, bool):
+ return value
+ self.error(obj, value)
+
+
+class CBool(Bool):
+ """A casting version of the boolean trait."""
+
+ def validate(self, obj, value):
+ try:
+ return bool(value)
+ except:
+ self.error(obj, value)
+
+
+class Enum(TraitType):
+ """An enum whose value must be in a given sequence."""
+
def __init__(self, values, default_value=Undefined, **kwargs):
- self.values = values
+ self.values = values
if kwargs.get('allow_none', False) and default_value is Undefined:
- default_value = None
+ default_value = None
super(Enum, self).__init__(default_value, **kwargs)
-
- def validate(self, obj, value):
- if value in self.values:
- return value
- self.error(obj, value)
-
- def info(self):
- """ Returns a description of the trait."""
- result = 'any of ' + repr(self.values)
- if self.allow_none:
- return result + ' or None'
- return result
-
-class CaselessStrEnum(Enum):
- """An enum of strings where the case should be ignored."""
+
+ def validate(self, obj, value):
+ if value in self.values:
+ return value
+ self.error(obj, value)
+
+ def info(self):
+ """ Returns a description of the trait."""
+ result = 'any of ' + repr(self.values)
+ if self.allow_none:
+ return result + ' or None'
+ return result
+
+class CaselessStrEnum(Enum):
+ """An enum of strings where the case should be ignored."""
def __init__(self, values, default_value=Undefined, **kwargs):
values = [cast_unicode_py2(value) for value in values]
super(CaselessStrEnum, self).__init__(values, default_value=default_value, **kwargs)
- def validate(self, obj, value):
- if isinstance(value, str):
+ def validate(self, obj, value):
+ if isinstance(value, str):
value = cast_unicode_py2(value)
if not isinstance(value, six.string_types):
- self.error(obj, value)
-
- for v in self.values:
- if v.lower() == value.lower():
- return v
- self.error(obj, value)
-
-class Container(Instance):
- """An instance of a container (list, set, etc.)
-
- To be subclassed by overriding klass.
- """
- klass = None
- _cast_types = ()
- _valid_defaults = SequenceTypes
- _trait = None
-
+ self.error(obj, value)
+
+ for v in self.values:
+ if v.lower() == value.lower():
+ return v
+ self.error(obj, value)
+
+class Container(Instance):
+ """An instance of a container (list, set, etc.)
+
+ To be subclassed by overriding klass.
+ """
+ klass = None
+ _cast_types = ()
+ _valid_defaults = SequenceTypes
+ _trait = None
+
def __init__(self, trait=None, default_value=None, **kwargs):
- """Create a container trait type from a list, set, or tuple.
-
- The default value is created by doing ``List(default_value)``,
- which creates a copy of the ``default_value``.
-
- ``trait`` can be specified, which restricts the type of elements
- in the container to that TraitType.
-
- If only one arg is given and it is not a Trait, it is taken as
- ``default_value``:
-
- ``c = List([1, 2, 3])``
-
- Parameters
- ----------
-
- trait : TraitType [ optional ]
- the type for restricting the contents of the Container. If unspecified,
- types are not checked.
-
- default_value : SequenceType [ optional ]
- The default value for the Trait. Must be list/tuple/set, and
- will be cast to the container type.
-
- allow_none : bool [ default False ]
- Whether to allow the value to be None
-
+ """Create a container trait type from a list, set, or tuple.
+
+ The default value is created by doing ``List(default_value)``,
+ which creates a copy of the ``default_value``.
+
+ ``trait`` can be specified, which restricts the type of elements
+ in the container to that TraitType.
+
+ If only one arg is given and it is not a Trait, it is taken as
+ ``default_value``:
+
+ ``c = List([1, 2, 3])``
+
+ Parameters
+ ----------
+
+ trait : TraitType [ optional ]
+ the type for restricting the contents of the Container. If unspecified,
+ types are not checked.
+
+ default_value : SequenceType [ optional ]
+ The default value for the Trait. Must be list/tuple/set, and
+ will be cast to the container type.
+
+ allow_none : bool [ default False ]
+ Whether to allow the value to be None
+
**kwargs : any
- further keys for extensions to the Trait (e.g. config)
-
- """
- # allow List([values]):
- if default_value is None and not is_trait(trait):
- default_value = trait
- trait = None
-
- if default_value is None:
- args = ()
- elif isinstance(default_value, self._valid_defaults):
- args = (default_value,)
- else:
- raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
-
- if is_trait(trait):
- if isinstance(trait, type):
+ further keys for extensions to the Trait (e.g. config)
+
+ """
+ # allow List([values]):
+ if default_value is None and not is_trait(trait):
+ default_value = trait
+ trait = None
+
+ if default_value is None:
+ args = ()
+ elif isinstance(default_value, self._valid_defaults):
+ args = (default_value,)
+ else:
+ raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
+
+ if is_trait(trait):
+ if isinstance(trait, type):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)."
" Passing types is deprecated in traitlets 4.1.",
- DeprecationWarning, stacklevel=3)
- self._trait = trait() if isinstance(trait, type) else trait
- elif trait is not None:
- raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait))
-
+ DeprecationWarning, stacklevel=3)
+ self._trait = trait() if isinstance(trait, type) else trait
+ elif trait is not None:
+ raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait))
+
super(Container,self).__init__(klass=self.klass, args=args, **kwargs)
-
- def element_error(self, obj, element, validator):
- e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
- % (self.name, class_of(obj), validator.info(), repr_type(element))
- raise TraitError(e)
-
- def validate(self, obj, value):
- if isinstance(value, self._cast_types):
- value = self.klass(value)
- value = super(Container, self).validate(obj, value)
- if value is None:
- return value
-
- value = self.validate_elements(obj, value)
-
- return value
-
- def validate_elements(self, obj, value):
- validated = []
- if self._trait is None or isinstance(self._trait, Any):
- return value
- for v in value:
- try:
- v = self._trait._validate(obj, v)
- except TraitError:
- self.element_error(obj, v, self._trait)
- else:
- validated.append(v)
- return self.klass(validated)
-
- def class_init(self, cls, name):
- if isinstance(self._trait, TraitType):
- self._trait.class_init(cls, None)
- super(Container, self).class_init(cls, name)
-
- def instance_init(self, obj):
- if isinstance(self._trait, TraitType):
- self._trait.instance_init(obj)
- super(Container, self).instance_init(obj)
-
-
-class List(Container):
- """An instance of a Python list."""
- klass = list
- _cast_types = (tuple,)
-
+
+ def element_error(self, obj, element, validator):
+ e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
+ % (self.name, class_of(obj), validator.info(), repr_type(element))
+ raise TraitError(e)
+
+ def validate(self, obj, value):
+ if isinstance(value, self._cast_types):
+ value = self.klass(value)
+ value = super(Container, self).validate(obj, value)
+ if value is None:
+ return value
+
+ value = self.validate_elements(obj, value)
+
+ return value
+
+ def validate_elements(self, obj, value):
+ validated = []
+ if self._trait is None or isinstance(self._trait, Any):
+ return value
+ for v in value:
+ try:
+ v = self._trait._validate(obj, v)
+ except TraitError:
+ self.element_error(obj, v, self._trait)
+ else:
+ validated.append(v)
+ return self.klass(validated)
+
+ def class_init(self, cls, name):
+ if isinstance(self._trait, TraitType):
+ self._trait.class_init(cls, None)
+ super(Container, self).class_init(cls, name)
+
+ def instance_init(self, obj):
+ if isinstance(self._trait, TraitType):
+ self._trait.instance_init(obj)
+ super(Container, self).instance_init(obj)
+
+
+class List(Container):
+ """An instance of a Python list."""
+ klass = list
+ _cast_types = (tuple,)
+
def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize, **kwargs):
- """Create a List trait type from a list, set, or tuple.
-
- The default value is created by doing ``list(default_value)``,
- which creates a copy of the ``default_value``.
-
- ``trait`` can be specified, which restricts the type of elements
- in the container to that TraitType.
-
- If only one arg is given and it is not a Trait, it is taken as
- ``default_value``:
-
- ``c = List([1, 2, 3])``
-
- Parameters
- ----------
-
- trait : TraitType [ optional ]
- the type for restricting the contents of the Container.
- If unspecified, types are not checked.
-
- default_value : SequenceType [ optional ]
- The default value for the Trait. Must be list/tuple/set, and
- will be cast to the container type.
-
- minlen : Int [ default 0 ]
- The minimum length of the input list
-
- maxlen : Int [ default sys.maxsize ]
- The maximum length of the input list
- """
- self._minlen = minlen
- self._maxlen = maxlen
- super(List, self).__init__(trait=trait, default_value=default_value,
+ """Create a List trait type from a list, set, or tuple.
+
+ The default value is created by doing ``list(default_value)``,
+ which creates a copy of the ``default_value``.
+
+ ``trait`` can be specified, which restricts the type of elements
+ in the container to that TraitType.
+
+ If only one arg is given and it is not a Trait, it is taken as
+ ``default_value``:
+
+ ``c = List([1, 2, 3])``
+
+ Parameters
+ ----------
+
+ trait : TraitType [ optional ]
+ the type for restricting the contents of the Container.
+ If unspecified, types are not checked.
+
+ default_value : SequenceType [ optional ]
+ The default value for the Trait. Must be list/tuple/set, and
+ will be cast to the container type.
+
+ minlen : Int [ default 0 ]
+ The minimum length of the input list
+
+ maxlen : Int [ default sys.maxsize ]
+ The maximum length of the input list
+ """
+ self._minlen = minlen
+ self._maxlen = maxlen
+ super(List, self).__init__(trait=trait, default_value=default_value,
**kwargs)
-
- def length_error(self, obj, value):
- e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
- % (self.name, class_of(obj), self._minlen, self._maxlen, value)
- raise TraitError(e)
-
- def validate_elements(self, obj, value):
- length = len(value)
- if length < self._minlen or length > self._maxlen:
- self.length_error(obj, value)
-
- return super(List, self).validate_elements(obj, value)
-
- def validate(self, obj, value):
- value = super(List, self).validate(obj, value)
- value = self.validate_elements(obj, value)
- return value
-
-
-class Set(List):
- """An instance of a Python set."""
- klass = set
- _cast_types = (tuple, list)
-
- # Redefine __init__ just to make the docstring more accurate.
- def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
+
+ def length_error(self, obj, value):
+ e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
+ % (self.name, class_of(obj), self._minlen, self._maxlen, value)
+ raise TraitError(e)
+
+ def validate_elements(self, obj, value):
+ length = len(value)
+ if length < self._minlen or length > self._maxlen:
+ self.length_error(obj, value)
+
+ return super(List, self).validate_elements(obj, value)
+
+ def validate(self, obj, value):
+ value = super(List, self).validate(obj, value)
+ value = self.validate_elements(obj, value)
+ return value
+
+
+class Set(List):
+ """An instance of a Python set."""
+ klass = set
+ _cast_types = (tuple, list)
+
+ # Redefine __init__ just to make the docstring more accurate.
+ def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
**kwargs):
- """Create a Set trait type from a list, set, or tuple.
-
- The default value is created by doing ``set(default_value)``,
- which creates a copy of the ``default_value``.
-
- ``trait`` can be specified, which restricts the type of elements
- in the container to that TraitType.
-
- If only one arg is given and it is not a Trait, it is taken as
- ``default_value``:
-
- ``c = Set({1, 2, 3})``
-
- Parameters
- ----------
-
- trait : TraitType [ optional ]
- the type for restricting the contents of the Container.
- If unspecified, types are not checked.
-
- default_value : SequenceType [ optional ]
- The default value for the Trait. Must be list/tuple/set, and
- will be cast to the container type.
-
- minlen : Int [ default 0 ]
- The minimum length of the input list
-
- maxlen : Int [ default sys.maxsize ]
- The maximum length of the input list
- """
+ """Create a Set trait type from a list, set, or tuple.
+
+ The default value is created by doing ``set(default_value)``,
+ which creates a copy of the ``default_value``.
+
+ ``trait`` can be specified, which restricts the type of elements
+ in the container to that TraitType.
+
+ If only one arg is given and it is not a Trait, it is taken as
+ ``default_value``:
+
+ ``c = Set({1, 2, 3})``
+
+ Parameters
+ ----------
+
+ trait : TraitType [ optional ]
+ the type for restricting the contents of the Container.
+ If unspecified, types are not checked.
+
+ default_value : SequenceType [ optional ]
+ The default value for the Trait. Must be list/tuple/set, and
+ will be cast to the container type.
+
+ minlen : Int [ default 0 ]
+ The minimum length of the input list
+
+ maxlen : Int [ default sys.maxsize ]
+ The maximum length of the input list
+ """
super(Set, self).__init__(trait, default_value, minlen, maxlen, **kwargs)
-
-
-class Tuple(Container):
- """An instance of a Python tuple."""
- klass = tuple
- _cast_types = (list,)
-
+
+
+class Tuple(Container):
+ """An instance of a Python tuple."""
+ klass = tuple
+ _cast_types = (list,)
+
def __init__(self, *traits, **kwargs):
- """Create a tuple from a list, set, or tuple.
-
- Create a fixed-type tuple with Traits:
-
- ``t = Tuple(Int(), Str(), CStr())``
-
- would be length 3, with Int,Str,CStr for each element.
-
- If only one arg is given and it is not a Trait, it is taken as
- default_value:
-
- ``t = Tuple((1, 2, 3))``
-
- Otherwise, ``default_value`` *must* be specified by keyword.
-
- Parameters
- ----------
-
- `*traits` : TraitTypes [ optional ]
- the types for restricting the contents of the Tuple. If unspecified,
- types are not checked. If specified, then each positional argument
- corresponds to an element of the tuple. Tuples defined with traits
- are of fixed length.
-
- default_value : SequenceType [ optional ]
- The default value for the Tuple. Must be list/tuple/set, and
- will be cast to a tuple. If ``traits`` are specified,
- ``default_value`` must conform to the shape and type they specify.
- """
+ """Create a tuple from a list, set, or tuple.
+
+ Create a fixed-type tuple with Traits:
+
+ ``t = Tuple(Int(), Str(), CStr())``
+
+ would be length 3, with Int,Str,CStr for each element.
+
+ If only one arg is given and it is not a Trait, it is taken as
+ default_value:
+
+ ``t = Tuple((1, 2, 3))``
+
+ Otherwise, ``default_value`` *must* be specified by keyword.
+
+ Parameters
+ ----------
+
+ `*traits` : TraitTypes [ optional ]
+ the types for restricting the contents of the Tuple. If unspecified,
+ types are not checked. If specified, then each positional argument
+ corresponds to an element of the tuple. Tuples defined with traits
+ are of fixed length.
+
+ default_value : SequenceType [ optional ]
+ The default value for the Tuple. Must be list/tuple/set, and
+ will be cast to a tuple. If ``traits`` are specified,
+ ``default_value`` must conform to the shape and type they specify.
+ """
default_value = kwargs.pop('default_value', Undefined)
- # allow Tuple((values,)):
- if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]):
- default_value = traits[0]
- traits = ()
-
- if default_value is Undefined:
- args = ()
- elif isinstance(default_value, self._valid_defaults):
- args = (default_value,)
- else:
- raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
-
- self._traits = []
- for trait in traits:
- if isinstance(trait, type):
+ # allow Tuple((values,)):
+ if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]):
+ default_value = traits[0]
+ traits = ()
+
+ if default_value is Undefined:
+ args = ()
+ elif isinstance(default_value, self._valid_defaults):
+ args = (default_value,)
+ else:
+ raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
+
+ self._traits = []
+ for trait in traits:
+ if isinstance(trait, type):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)"
" Passing types is deprecated in traitlets 4.1.",
- DeprecationWarning, stacklevel=2)
- t = trait() if isinstance(trait, type) else trait
- self._traits.append(t)
-
- if self._traits and default_value is None:
- # don't allow default to be an empty container if length is specified
- args = None
+ DeprecationWarning, stacklevel=2)
+ t = trait() if isinstance(trait, type) else trait
+ self._traits.append(t)
+
+ if self._traits and default_value is None:
+ # don't allow default to be an empty container if length is specified
+ args = None
super(Container,self).__init__(klass=self.klass, args=args, **kwargs)
-
- def validate_elements(self, obj, value):
- if not self._traits:
- # nothing to validate
- return value
- if len(value) != len(self._traits):
- e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
- % (self.name, class_of(obj), len(self._traits), repr_type(value))
- raise TraitError(e)
-
- validated = []
- for t, v in zip(self._traits, value):
- try:
- v = t._validate(obj, v)
- except TraitError:
- self.element_error(obj, v, t)
- else:
- validated.append(v)
- return tuple(validated)
-
- def class_init(self, cls, name):
- for trait in self._traits:
- if isinstance(trait, TraitType):
- trait.class_init(cls, None)
- super(Container, self).class_init(cls, name)
-
- def instance_init(self, obj):
- for trait in self._traits:
- if isinstance(trait, TraitType):
- trait.instance_init(obj)
- super(Container, self).instance_init(obj)
-
-
-class Dict(Instance):
- """An instance of a Python dict."""
- _trait = None
-
- def __init__(self, trait=None, traits=None, default_value=Undefined,
+
+ def validate_elements(self, obj, value):
+ if not self._traits:
+ # nothing to validate
+ return value
+ if len(value) != len(self._traits):
+ e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
+ % (self.name, class_of(obj), len(self._traits), repr_type(value))
+ raise TraitError(e)
+
+ validated = []
+ for t, v in zip(self._traits, value):
+ try:
+ v = t._validate(obj, v)
+ except TraitError:
+ self.element_error(obj, v, t)
+ else:
+ validated.append(v)
+ return tuple(validated)
+
+ def class_init(self, cls, name):
+ for trait in self._traits:
+ if isinstance(trait, TraitType):
+ trait.class_init(cls, None)
+ super(Container, self).class_init(cls, name)
+
+ def instance_init(self, obj):
+ for trait in self._traits:
+ if isinstance(trait, TraitType):
+ trait.instance_init(obj)
+ super(Container, self).instance_init(obj)
+
+
+class Dict(Instance):
+ """An instance of a Python dict."""
+ _trait = None
+
+ def __init__(self, trait=None, traits=None, default_value=Undefined,
**kwargs):
"""Create a dict trait type from a Python dict.
-
- The default value is created by doing ``dict(default_value)``,
- which creates a copy of the ``default_value``.
-
+
+ The default value is created by doing ``dict(default_value)``,
+ which creates a copy of the ``default_value``.
+
Parameters
----------
- trait : TraitType [ optional ]
+ trait : TraitType [ optional ]
The specified trait type to check and use to restrict contents of
the Container. If unspecified, trait types are not checked.
-
+
traits : Dictionary of trait types [ optional ]
A Python dictionary containing the types that are valid for
restricting the content of the Dict Container for certain keys.
-
- default_value : SequenceType [ optional ]
- The default value for the Dict. Must be dict, tuple, or None, and
- will be cast to a dict if not None. If `trait` is specified, the
- `default_value` must conform to the constraints it specifies.
- """
- # Handling positional arguments
- if default_value is Undefined and trait is not None:
- if not is_trait(trait):
- default_value = trait
- trait = None
-
- # Handling default value
- if default_value is Undefined:
- default_value = {}
- if default_value is None:
- args = None
- elif isinstance(default_value, dict):
- args = (default_value,)
- elif isinstance(default_value, SequenceTypes):
- args = (default_value,)
- else:
- raise TypeError('default value of Dict was %s' % default_value)
-
- # Case where a type of TraitType is provided rather than an instance
- if is_trait(trait):
- if isinstance(trait, type):
+
+ default_value : SequenceType [ optional ]
+ The default value for the Dict. Must be dict, tuple, or None, and
+ will be cast to a dict if not None. If `trait` is specified, the
+ `default_value` must conform to the constraints it specifies.
+ """
+ # Handling positional arguments
+ if default_value is Undefined and trait is not None:
+ if not is_trait(trait):
+ default_value = trait
+ trait = None
+
+ # Handling default value
+ if default_value is Undefined:
+ default_value = {}
+ if default_value is None:
+ args = None
+ elif isinstance(default_value, dict):
+ args = (default_value,)
+ elif isinstance(default_value, SequenceTypes):
+ args = (default_value,)
+ else:
+ raise TypeError('default value of Dict was %s' % default_value)
+
+ # Case where a type of TraitType is provided rather than an instance
+ if is_trait(trait):
+ if isinstance(trait, type):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)"
" Passing types is deprecated in traitlets 4.1.",
- DeprecationWarning, stacklevel=2)
- self._trait = trait() if isinstance(trait, type) else trait
- elif trait is not None:
- raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait))
-
- self._traits = traits
-
+ DeprecationWarning, stacklevel=2)
+ self._trait = trait() if isinstance(trait, type) else trait
+ elif trait is not None:
+ raise TypeError("`trait` must be a Trait or None, got %s" % repr_type(trait))
+
+ self._traits = traits
+
super(Dict, self).__init__(klass=dict, args=args, **kwargs)
-
- def element_error(self, obj, element, validator):
- e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
- % (self.name, class_of(obj), validator.info(), repr_type(element))
- raise TraitError(e)
-
- def validate(self, obj, value):
- value = super(Dict, self).validate(obj, value)
- if value is None:
- return value
- value = self.validate_elements(obj, value)
- return value
-
- def validate_elements(self, obj, value):
+
+ def element_error(self, obj, element, validator):
+ e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
+ % (self.name, class_of(obj), validator.info(), repr_type(element))
+ raise TraitError(e)
+
+ def validate(self, obj, value):
+ value = super(Dict, self).validate(obj, value)
+ if value is None:
+ return value
+ value = self.validate_elements(obj, value)
+ return value
+
+ def validate_elements(self, obj, value):
use_dict = bool(self._traits)
default_to = (self._trait or Any())
if not use_dict and isinstance(default_to, Any):
- return value
+ return value
- validated = {}
- for key in value:
+ validated = {}
+ for key in value:
if use_dict and key in self._traits:
validate_with = self._traits[key]
else:
validate_with = default_to
- try:
+ try:
v = value[key]
if not isinstance(validate_with, Any):
v = validate_with._validate(obj, v)
- except TraitError:
+ except TraitError:
self.element_error(obj, v, validate_with)
- else:
- validated[key] = v
-
- return self.klass(validated)
-
- def class_init(self, cls, name):
- if isinstance(self._trait, TraitType):
- self._trait.class_init(cls, None)
- if self._traits is not None:
- for trait in self._traits.values():
- trait.class_init(cls, None)
- super(Dict, self).class_init(cls, name)
-
- def instance_init(self, obj):
- if isinstance(self._trait, TraitType):
- self._trait.instance_init(obj)
- if self._traits is not None:
- for trait in self._traits.values():
- trait.instance_init(obj)
- super(Dict, self).instance_init(obj)
-
-
-class TCPAddress(TraitType):
- """A trait for an (ip, port) tuple.
-
- This allows for both IPv4 IP addresses as well as hostnames.
- """
-
- default_value = ('127.0.0.1', 0)
- info_text = 'an (ip, port) tuple'
-
- def validate(self, obj, value):
- if isinstance(value, tuple):
- if len(value) == 2:
+ else:
+ validated[key] = v
+
+ return self.klass(validated)
+
+ def class_init(self, cls, name):
+ if isinstance(self._trait, TraitType):
+ self._trait.class_init(cls, None)
+ if self._traits is not None:
+ for trait in self._traits.values():
+ trait.class_init(cls, None)
+ super(Dict, self).class_init(cls, name)
+
+ def instance_init(self, obj):
+ if isinstance(self._trait, TraitType):
+ self._trait.instance_init(obj)
+ if self._traits is not None:
+ for trait in self._traits.values():
+ trait.instance_init(obj)
+ super(Dict, self).instance_init(obj)
+
+
+class TCPAddress(TraitType):
+ """A trait for an (ip, port) tuple.
+
+ This allows for both IPv4 IP addresses as well as hostnames.
+ """
+
+ default_value = ('127.0.0.1', 0)
+ info_text = 'an (ip, port) tuple'
+
+ def validate(self, obj, value):
+ if isinstance(value, tuple):
+ if len(value) == 2:
if isinstance(value[0], six.string_types) and isinstance(value[1], int):
- port = value[1]
- if port >= 0 and port <= 65535:
- return value
- self.error(obj, value)
-
-class CRegExp(TraitType):
- """A casting compiled regular expression trait.
-
- Accepts both strings and compiled regular expressions. The resulting
- attribute will be a compiled regular expression."""
-
- info_text = 'a regular expression'
-
- def validate(self, obj, value):
- try:
- return re.compile(value)
- except:
- self.error(obj, value)
+ port = value[1]
+ if port >= 0 and port <= 65535:
+ return value
+ self.error(obj, value)
+
+class CRegExp(TraitType):
+ """A casting compiled regular expression trait.
+
+ Accepts both strings and compiled regular expressions. The resulting
+ attribute will be a compiled regular expression."""
+
+ info_text = 'a regular expression'
+
+ def validate(self, obj, value):
+ try:
+ return re.compile(value)
+ except:
+ self.error(obj, value)
class UseEnum(TraitType):
diff --git a/contrib/python/traitlets/py2/traitlets/utils/getargspec.py b/contrib/python/traitlets/py2/traitlets/utils/getargspec.py
index 0a047379fe..5c79b29549 100644
--- a/contrib/python/traitlets/py2/traitlets/utils/getargspec.py
+++ b/contrib/python/traitlets/py2/traitlets/utils/getargspec.py
@@ -1,86 +1,86 @@
-# -*- coding: utf-8 -*-
-"""
- getargspec excerpted from:
-
- sphinx.util.inspect
- ~~~~~~~~~~~~~~~~~~~
- Helpers for inspecting Python modules.
- :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import inspect
+# -*- coding: utf-8 -*-
+"""
+ getargspec excerpted from:
+
+ sphinx.util.inspect
+ ~~~~~~~~~~~~~~~~~~~
+ Helpers for inspecting Python modules.
+ :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import inspect
from six import PY3
-
-# Unmodified from sphinx below this line
-
-if PY3:
- from functools import partial
-
- def getargspec(func):
- """Like inspect.getargspec but supports functools.partial as well."""
- if inspect.ismethod(func):
- func = func.__func__
- if type(func) is partial:
- orig_func = func.func
- argspec = getargspec(orig_func)
- args = list(argspec[0])
- defaults = list(argspec[3] or ())
- kwoargs = list(argspec[4])
- kwodefs = dict(argspec[5] or {})
- if func.args:
- args = args[len(func.args):]
- for arg in func.keywords or ():
- try:
- i = args.index(arg) - len(args)
- del args[i]
- try:
- del defaults[i]
- except IndexError:
- pass
- except ValueError: # must be a kwonly arg
- i = kwoargs.index(arg)
- del kwoargs[i]
- del kwodefs[arg]
- return inspect.FullArgSpec(args, argspec[1], argspec[2],
- tuple(defaults), kwoargs,
- kwodefs, argspec[6])
- while hasattr(func, '__wrapped__'):
- func = func.__wrapped__
- if not inspect.isfunction(func):
- raise TypeError('%r is not a Python function' % func)
- return inspect.getfullargspec(func)
-
-else: # 2.6, 2.7
- from functools import partial
-
- def getargspec(func):
- """Like inspect.getargspec but supports functools.partial as well."""
- if inspect.ismethod(func):
- func = func.__func__
- parts = 0, ()
- if type(func) is partial:
- keywords = func.keywords
- if keywords is None:
- keywords = {}
- parts = len(func.args), keywords.keys()
- func = func.func
- if not inspect.isfunction(func):
- raise TypeError('%r is not a Python function' % func)
- args, varargs, varkw = inspect.getargs(func.__code__)
- func_defaults = func.__defaults__
- if func_defaults is None:
- func_defaults = []
- else:
- func_defaults = list(func_defaults)
- if parts[0]:
- args = args[parts[0]:]
- if parts[1]:
- for arg in parts[1]:
- i = args.index(arg) - len(args)
- del args[i]
- try:
- del func_defaults[i]
- except IndexError:
- pass
- return inspect.ArgSpec(args, varargs, varkw, func_defaults)
+
+# Unmodified from sphinx below this line
+
+if PY3:
+ from functools import partial
+
+ def getargspec(func):
+ """Like inspect.getargspec but supports functools.partial as well."""
+ if inspect.ismethod(func):
+ func = func.__func__
+ if type(func) is partial:
+ orig_func = func.func
+ argspec = getargspec(orig_func)
+ args = list(argspec[0])
+ defaults = list(argspec[3] or ())
+ kwoargs = list(argspec[4])
+ kwodefs = dict(argspec[5] or {})
+ if func.args:
+ args = args[len(func.args):]
+ for arg in func.keywords or ():
+ try:
+ i = args.index(arg) - len(args)
+ del args[i]
+ try:
+ del defaults[i]
+ except IndexError:
+ pass
+ except ValueError: # must be a kwonly arg
+ i = kwoargs.index(arg)
+ del kwoargs[i]
+ del kwodefs[arg]
+ return inspect.FullArgSpec(args, argspec[1], argspec[2],
+ tuple(defaults), kwoargs,
+ kwodefs, argspec[6])
+ while hasattr(func, '__wrapped__'):
+ func = func.__wrapped__
+ if not inspect.isfunction(func):
+ raise TypeError('%r is not a Python function' % func)
+ return inspect.getfullargspec(func)
+
+else: # 2.6, 2.7
+ from functools import partial
+
+ def getargspec(func):
+ """Like inspect.getargspec but supports functools.partial as well."""
+ if inspect.ismethod(func):
+ func = func.__func__
+ parts = 0, ()
+ if type(func) is partial:
+ keywords = func.keywords
+ if keywords is None:
+ keywords = {}
+ parts = len(func.args), keywords.keys()
+ func = func.func
+ if not inspect.isfunction(func):
+ raise TypeError('%r is not a Python function' % func)
+ args, varargs, varkw = inspect.getargs(func.__code__)
+ func_defaults = func.__defaults__
+ if func_defaults is None:
+ func_defaults = []
+ else:
+ func_defaults = list(func_defaults)
+ if parts[0]:
+ args = args[parts[0]:]
+ if parts[1]:
+ for arg in parts[1]:
+ i = args.index(arg) - len(args)
+ del args[i]
+ try:
+ del func_defaults[i]
+ except IndexError:
+ pass
+ return inspect.ArgSpec(args, varargs, varkw, func_defaults)
diff --git a/contrib/python/traitlets/py2/traitlets/utils/importstring.py b/contrib/python/traitlets/py2/traitlets/utils/importstring.py
index 5b4f643f41..873ab9635c 100644
--- a/contrib/python/traitlets/py2/traitlets/utils/importstring.py
+++ b/contrib/python/traitlets/py2/traitlets/utils/importstring.py
@@ -1,42 +1,42 @@
-# encoding: utf-8
-"""
-A simple utility to import something by its string name.
-"""
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
+# encoding: utf-8
+"""
+A simple utility to import something by its string name.
+"""
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
from ipython_genutils.py3compat import cast_bytes_py2
from six import string_types
-
-def import_item(name):
- """Import and return ``bar`` given the string ``foo.bar``.
-
- Calling ``bar = import_item("foo.bar")`` is the functional equivalent of
- executing the code ``from foo import bar``.
-
- Parameters
- ----------
- name : string
- The fully qualified name of the module/package being imported.
-
- Returns
- -------
- mod : module object
- The module that was imported.
- """
- if not isinstance(name, string_types):
- raise TypeError("import_item accepts strings, not '%s'." % type(name))
- name = cast_bytes_py2(name)
- parts = name.rsplit('.', 1)
- if len(parts) == 2:
- # called with 'foo.bar....'
- package, obj = parts
- module = __import__(package, fromlist=[obj])
- try:
- pak = getattr(module, obj)
- except AttributeError:
- raise ImportError('No module named %s' % obj)
- return pak
- else:
- # called with un-dotted string
- return __import__(parts[0])
+
+def import_item(name):
+ """Import and return ``bar`` given the string ``foo.bar``.
+
+ Calling ``bar = import_item("foo.bar")`` is the functional equivalent of
+ executing the code ``from foo import bar``.
+
+ Parameters
+ ----------
+ name : string
+ The fully qualified name of the module/package being imported.
+
+ Returns
+ -------
+ mod : module object
+ The module that was imported.
+ """
+ if not isinstance(name, string_types):
+ raise TypeError("import_item accepts strings, not '%s'." % type(name))
+ name = cast_bytes_py2(name)
+ parts = name.rsplit('.', 1)
+ if len(parts) == 2:
+ # called with 'foo.bar....'
+ package, obj = parts
+ module = __import__(package, fromlist=[obj])
+ try:
+ pak = getattr(module, obj)
+ except AttributeError:
+ raise ImportError('No module named %s' % obj)
+ return pak
+ else:
+ # called with un-dotted string
+ return __import__(parts[0])
diff --git a/contrib/python/traitlets/py2/traitlets/utils/sentinel.py b/contrib/python/traitlets/py2/traitlets/utils/sentinel.py
index dc57a2591c..7af2558c1a 100644
--- a/contrib/python/traitlets/py2/traitlets/utils/sentinel.py
+++ b/contrib/python/traitlets/py2/traitlets/utils/sentinel.py
@@ -1,17 +1,17 @@
-"""Sentinel class for constants with useful reprs"""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-class Sentinel(object):
-
- def __init__(self, name, module, docstring=None):
- self.name = name
- self.module = module
- if docstring:
- self.__doc__ = docstring
-
-
- def __repr__(self):
- return str(self.module)+'.'+self.name
-
+"""Sentinel class for constants with useful reprs"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+class Sentinel(object):
+
+ def __init__(self, name, module, docstring=None):
+ self.name = name
+ self.module = module
+ if docstring:
+ self.__doc__ = docstring
+
+
+ def __repr__(self):
+ return str(self.module)+'.'+self.name
+
diff --git a/contrib/python/traitlets/py2/ya.make b/contrib/python/traitlets/py2/ya.make
index 4a60107101..ae62d588b4 100644
--- a/contrib/python/traitlets/py2/ya.make
+++ b/contrib/python/traitlets/py2/ya.make
@@ -1,49 +1,49 @@
# Generated by devtools/yamaker (pypi).
PY2_LIBRARY()
-
+
PROVIDES(python_traitlets)
OWNER(borman nslus g:python-contrib)
VERSION(4.3.3)
-
+
LICENSE(BSD-3-Clause)
-PEERDIR(
+PEERDIR(
contrib/python/decorator
contrib/python/enum34
contrib/python/ipython-genutils
contrib/python/six
-)
-
+)
+
NO_LINT()
-PY_SRCS(
- TOP_LEVEL
- traitlets/__init__.py
- traitlets/_version.py
- traitlets/config/__init__.py
- traitlets/config/application.py
- traitlets/config/configurable.py
- traitlets/config/loader.py
- traitlets/config/manager.py
- traitlets/log.py
- traitlets/traitlets.py
- traitlets/utils/__init__.py
+PY_SRCS(
+ TOP_LEVEL
+ traitlets/__init__.py
+ traitlets/_version.py
+ traitlets/config/__init__.py
+ traitlets/config/application.py
+ traitlets/config/configurable.py
+ traitlets/config/loader.py
+ traitlets/config/manager.py
+ traitlets/log.py
+ traitlets/traitlets.py
+ traitlets/utils/__init__.py
traitlets/utils/bunch.py
- traitlets/utils/getargspec.py
- traitlets/utils/importstring.py
- traitlets/utils/sentinel.py
-)
-
+ traitlets/utils/getargspec.py
+ traitlets/utils/importstring.py
+ traitlets/utils/sentinel.py
+)
+
RESOURCE_FILES(
PREFIX contrib/python/traitlets/py2/
.dist-info/METADATA
.dist-info/top_level.txt
)
-END()
+END()
RECURSE_FOR_TESTS(
tests
diff --git a/contrib/python/traitlets/py3/COPYING.md b/contrib/python/traitlets/py3/COPYING.md
index 39ca730a63..e314a9d376 100644
--- a/contrib/python/traitlets/py3/COPYING.md
+++ b/contrib/python/traitlets/py3/COPYING.md
@@ -1,62 +1,62 @@
-# Licensing terms
-
-Traitlets is adapted from enthought.traits, Copyright (c) Enthought, Inc.,
-under the terms of the Modified BSD License.
-
-This project is licensed under the terms of the Modified BSD License
-(also known as New or Revised or 3-Clause BSD), as follows:
-
-- Copyright (c) 2001-, IPython Development Team
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or
-other materials provided with the distribution.
-
-Neither the name of the IPython Development Team nor the names of its
-contributors may be used to endorse or promote products derived from this
-software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-## About the IPython Development Team
-
-The IPython Development Team is the set of all contributors to the IPython project.
-This includes all of the IPython subprojects.
-
-The core team that coordinates development on GitHub can be found here:
-https://github.com/jupyter/.
-
-## Our Copyright Policy
-
-IPython uses a shared copyright model. Each contributor maintains copyright
-over their contributions to IPython. But, it is important to note that these
-contributions are typically only changes to the repositories. Thus, the IPython
-source code, in its entirety is not the copyright of any single person or
-institution. Instead, it is the collective copyright of the entire IPython
-Development Team. If individual contributors want to maintain a record of what
-changes/contributions they have specific copyright on, they should indicate
-their copyright in the commit message of the change, when they commit the
-change to one of the IPython repositories.
-
-With this in mind, the following banner should be used in any source code file
-to indicate the copyright and license terms:
-
- # Copyright (c) IPython Development Team.
- # Distributed under the terms of the Modified BSD License.
+# Licensing terms
+
+Traitlets is adapted from enthought.traits, Copyright (c) Enthought, Inc.,
+under the terms of the Modified BSD License.
+
+This project is licensed under the terms of the Modified BSD License
+(also known as New or Revised or 3-Clause BSD), as follows:
+
+- Copyright (c) 2001-, IPython Development Team
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+Neither the name of the IPython Development Team nor the names of its
+contributors may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+## About the IPython Development Team
+
+The IPython Development Team is the set of all contributors to the IPython project.
+This includes all of the IPython subprojects.
+
+The core team that coordinates development on GitHub can be found here:
+https://github.com/jupyter/.
+
+## Our Copyright Policy
+
+IPython uses a shared copyright model. Each contributor maintains copyright
+over their contributions to IPython. But, it is important to note that these
+contributions are typically only changes to the repositories. Thus, the IPython
+source code, in its entirety is not the copyright of any single person or
+institution. Instead, it is the collective copyright of the entire IPython
+Development Team. If individual contributors want to maintain a record of what
+changes/contributions they have specific copyright on, they should indicate
+their copyright in the commit message of the change, when they commit the
+change to one of the IPython repositories.
+
+With this in mind, the following banner should be used in any source code file
+to indicate the copyright and license terms:
+
+ # Copyright (c) IPython Development Team.
+ # Distributed under the terms of the Modified BSD License.
diff --git a/contrib/python/traitlets/py3/traitlets/__init__.py b/contrib/python/traitlets/py3/traitlets/__init__.py
index ad5ba73c86..1b8675a879 100644
--- a/contrib/python/traitlets/py3/traitlets/__init__.py
+++ b/contrib/python/traitlets/py3/traitlets/__init__.py
@@ -1,11 +1,11 @@
from warnings import warn
from . import traitlets
-from .traitlets import *
-from .utils.importstring import import_item
+from .traitlets import *
+from .utils.importstring import import_item
from .utils.decorators import signature_has_traits
from .utils.bunch import Bunch
-from ._version import version_info, __version__
+from ._version import version_info, __version__
class Sentinel(traitlets.Sentinel):
diff --git a/contrib/python/traitlets/py3/traitlets/config/__init__.py b/contrib/python/traitlets/py3/traitlets/config/__init__.py
index 0ae7d63171..1531ee5930 100644
--- a/contrib/python/traitlets/py3/traitlets/config/__init__.py
+++ b/contrib/python/traitlets/py3/traitlets/config/__init__.py
@@ -1,8 +1,8 @@
-# encoding: utf-8
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-from .application import *
-from .configurable import *
-from .loader import Config
+# encoding: utf-8
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+from .application import *
+from .configurable import *
+from .loader import Config
diff --git a/contrib/python/traitlets/py3/traitlets/config/application.py b/contrib/python/traitlets/py3/traitlets/config/application.py
index 99a6ef7ee0..6cdb801008 100644
--- a/contrib/python/traitlets/py3/traitlets/config/application.py
+++ b/contrib/python/traitlets/py3/traitlets/config/application.py
@@ -1,68 +1,68 @@
-"""A base class for a configurable application."""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-
+"""A base class for a configurable application."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+
from collections import defaultdict, OrderedDict
from copy import deepcopy
import functools
-import json
-import logging
-import os
+import json
+import logging
+import os
import pprint
-import re
-import sys
+import re
+import sys
import warnings
-
-from traitlets.config.configurable import Configurable, SingletonConfigurable
-from traitlets.config.loader import (
- KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, JSONFileConfigLoader
-)
-from traitlets.traitlets import (
+
+from traitlets.config.configurable import Configurable, SingletonConfigurable
+from traitlets.config.loader import (
+ KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, JSONFileConfigLoader
+)
+from traitlets.traitlets import (
Bool, Unicode, List, Enum, Dict, Instance, TraitError, observe, observe_compat, default,
-)
+)
from ..utils.importstring import import_item
from ..utils import cast_unicode
from traitlets.utils.text import indent, wrap_paragraphs
from textwrap import dedent
+
-
-#-----------------------------------------------------------------------------
-# Descriptions for the various sections
-#-----------------------------------------------------------------------------
-# merge flags&aliases into options
-option_description = """
+#-----------------------------------------------------------------------------
+# Descriptions for the various sections
+#-----------------------------------------------------------------------------
+# merge flags&aliases into options
+option_description = """
The options below are convenience aliases to configurable class-options,
as listed in the "Equivalent to" description-line of the aliases.
To see all configurable class-options for some <cmd>, use:
<cmd> --help-all
""".strip() # trim newlines of front and back
-
-keyvalue_description = """
+
+keyvalue_description = """
The command-line option below sets the respective configurable class-parameter:
--Class.parameter=value
This line is evaluated in Python, so simple expressions are allowed.
For instance, to set `C.a=[0,1,2]`, you may type this:
--C.a='range(3)'
-""".strip() # trim newlines of front and back
-
-# sys.argv can be missing, for example when python is embedded. See the docs
-# for details: http://docs.python.org/2/c-api/intro.html#embedding-python
-if not hasattr(sys, "argv"):
- sys.argv = [""]
-
-subcommand_description = """
-Subcommands are launched as `{app} cmd [args]`. For information on using
-subcommand 'cmd', do: `{app} cmd -h`.
-"""
-# get running program name
-
-#-----------------------------------------------------------------------------
-# Application class
-#-----------------------------------------------------------------------------
-
+""".strip() # trim newlines of front and back
+
+# sys.argv can be missing, for example when python is embedded. See the docs
+# for details: http://docs.python.org/2/c-api/intro.html#embedding-python
+if not hasattr(sys, "argv"):
+ sys.argv = [""]
+
+subcommand_description = """
+Subcommands are launched as `{app} cmd [args]`. For information on using
+subcommand 'cmd', do: `{app} cmd -h`.
+"""
+# get running program name
+
+#-----------------------------------------------------------------------------
+# Application class
+#-----------------------------------------------------------------------------
+
_envvar = os.environ.get('TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR','')
@@ -75,13 +75,13 @@ else:
def catch_config_error(method):
- """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
-
- On a TraitError (generally caused by bad config), this will print the trait's
- message, and exit the app.
+ """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
+
+ On a TraitError (generally caused by bad config), this will print the trait's
+ message, and exit the app.
- For use on init methods, to prevent invoking excepthook on invalid input.
- """
+ For use on init methods, to prevent invoking excepthook on invalid input.
+ """
@functools.wraps(method)
def inner(app, *args, **kwargs):
try:
@@ -90,116 +90,116 @@ def catch_config_error(method):
app.log.fatal("Bad config encountered during initialization: %s", e)
app.log.debug("Config at the time: %s", app.config)
app.exit(1)
-
+
return inner
-
-class ApplicationError(Exception):
- pass
-
-
-class LevelFormatter(logging.Formatter):
- """Formatter with additional `highlevel` record
-
- This field is empty if log level is less than highlevel_limit,
- otherwise it is formatted with self.highlevel_format.
-
- Useful for adding 'WARNING' to warning messages,
- without adding 'INFO' to info, etc.
- """
- highlevel_limit = logging.WARN
- highlevel_format = " %(levelname)s |"
-
- def format(self, record):
- if record.levelno >= self.highlevel_limit:
- record.highlevel = self.highlevel_format % record.__dict__
- else:
- record.highlevel = ""
- return super(LevelFormatter, self).format(record)
-
-
-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
+
+class ApplicationError(Exception):
+ pass
+
+
+class LevelFormatter(logging.Formatter):
+ """Formatter with additional `highlevel` record
+
+ This field is empty if log level is less than highlevel_limit,
+ otherwise it is formatted with self.highlevel_format.
+
+ Useful for adding 'WARNING' to warning messages,
+ without adding 'INFO' to info, etc.
+ """
+ highlevel_limit = logging.WARN
+ highlevel_format = " %(levelname)s |"
+
+ def format(self, record):
+ if record.levelno >= self.highlevel_limit:
+ record.highlevel = self.highlevel_format % record.__dict__
+ else:
+ record.highlevel = ""
+ return super(LevelFormatter, self).format(record)
+
+
+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
name = Unicode('application')
-
- # The description of the application that is printed at the beginning
- # of the help.
+
+ # The description of the application that is printed at the beginning
+ # of the help.
description = Unicode('This is an application.')
- # default section descriptions
- option_description = Unicode(option_description)
- keyvalue_description = Unicode(keyvalue_description)
- subcommand_description = Unicode(subcommand_description)
-
- python_config_loader_class = PyFileConfigLoader
- json_config_loader_class = JSONFileConfigLoader
-
- # The usage and example string that goes at the end of the help string.
- examples = Unicode()
-
- # A sequence of Configurable subclasses whose config=True attributes will
- # be exposed at the command line.
- classes = []
-
+ # default section descriptions
+ option_description = Unicode(option_description)
+ keyvalue_description = Unicode(keyvalue_description)
+ subcommand_description = Unicode(subcommand_description)
+
+ python_config_loader_class = PyFileConfigLoader
+ json_config_loader_class = JSONFileConfigLoader
+
+ # The usage and example string that goes at the end of the help string.
+ examples = Unicode()
+
+ # A sequence of Configurable subclasses whose config=True attributes will
+ # be exposed at the command line.
+ classes = []
+
def _classes_inc_parents(self, classes=None):
- """Iterate through configurable classes, including configurable parents
-
+ """Iterate through configurable classes, including configurable parents
+
:param classes:
The list of classes to iterate; if not set, uses :attr:`classes`.
- Children should always be after parents, and each class should only be
- yielded once.
- """
+ Children should always be after parents, and each class should only be
+ yielded once.
+ """
if classes is None:
classes = self.classes
- seen = set()
+ seen = set()
for c in classes:
- # We want to sort parents before children, so we reverse the MRO
- for parent in reversed(c.mro()):
- if issubclass(parent, Configurable) and (parent not in seen):
- seen.add(parent)
- yield parent
-
- # The version string of this application.
+ # We want to sort parents before children, so we reverse the MRO
+ for parent in reversed(c.mro()):
+ if issubclass(parent, Configurable) and (parent not in seen):
+ seen.add(parent)
+ yield parent
+
+ # The version string of this application.
version = Unicode('0.0')
- # the argv used to initialize the application
- argv = List()
-
+ # the argv used to initialize the application
+ argv = List()
+
# Whether failing to load config files should prevent startup
raise_config_file_errors = Bool(TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR)
- # The log level for the application
- log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
- default_value=logging.WARN,
- help="Set the log level by value or name.").tag(config=True)
-
- @observe('log_level')
- @observe_compat
- def _log_level_changed(self, change):
- """Adjust the log level when log_level is set."""
+ # The log level for the application
+ log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
+ default_value=logging.WARN,
+ help="Set the log level by value or name.").tag(config=True)
+
+ @observe('log_level')
+ @observe_compat
+ def _log_level_changed(self, change):
+ """Adjust the log level when log_level is set."""
new = change.new
if isinstance(new, str):
- new = getattr(logging, new)
- self.log_level = new
- self.log.setLevel(new)
+ new = getattr(logging, new)
+ self.log_level = new
+ self.log.setLevel(new)
- _log_formatter_cls = LevelFormatter
+ _log_formatter_cls = LevelFormatter
log_datefmt = Unicode("%Y-%m-%d %H:%M:%S",
- help="The date format used by logging formatters for %(asctime)s"
- ).tag(config=True)
-
- log_format = Unicode("[%(name)s]%(highlevel)s %(message)s",
- help="The Logging format template",
- ).tag(config=True)
-
- @observe('log_datefmt', 'log_format')
- @observe_compat
- def _log_format_changed(self, change):
- """Change the log formatter when log_format is set."""
+ help="The date format used by logging formatters for %(asctime)s"
+ ).tag(config=True)
+
+ log_format = Unicode("[%(name)s]%(highlevel)s %(message)s",
+ help="The Logging format template",
+ ).tag(config=True)
+
+ @observe('log_datefmt', 'log_format')
+ @observe_compat
+ def _log_format_changed(self, change):
+ """Change the log formatter when log_format is set."""
_log_handler = self._get_log_handler()
if not _log_handler:
warnings.warn(
@@ -207,48 +207,48 @@ class Application(SingletonConfigurable):
RuntimeWarning,
)
return
- _log_formatter = self._log_formatter_cls(fmt=self.log_format, datefmt=self.log_datefmt)
- _log_handler.setFormatter(_log_formatter)
-
- @default('log')
- def _log_default(self):
- """Start logging for this application.
-
- The default is to log to stderr using a StreamHandler, if no default
- handler already exists. The log level starts at logging.WARN, but this
- can be adjusted by setting the ``log_level`` attribute.
- """
- log = logging.getLogger(self.__class__.__name__)
- log.setLevel(self.log_level)
- log.propagate = False
- _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
- while _log:
- if _log.handlers:
- return log
- if not _log.propagate:
- break
- else:
- _log = _log.parent
+ _log_formatter = self._log_formatter_cls(fmt=self.log_format, datefmt=self.log_datefmt)
+ _log_handler.setFormatter(_log_formatter)
+
+ @default('log')
+ def _log_default(self):
+ """Start logging for this application.
+
+ The default is to log to stderr using a StreamHandler, if no default
+ handler already exists. The log level starts at logging.WARN, but this
+ can be adjusted by setting the ``log_level`` attribute.
+ """
+ log = logging.getLogger(self.__class__.__name__)
+ log.setLevel(self.log_level)
+ log.propagate = False
+ _log = log # copied from Logger.hasHandlers() (new in Python 3.2)
+ while _log:
+ if _log.handlers:
+ return log
+ if not _log.propagate:
+ break
+ else:
+ _log = _log.parent
if sys.executable and sys.executable.endswith('pythonw.exe'):
- # this should really go to a file, but file-logging is only
- # hooked up in parallel applications
- _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
- else:
- _log_handler = logging.StreamHandler()
- _log_formatter = self._log_formatter_cls(fmt=self.log_format, datefmt=self.log_datefmt)
- _log_handler.setFormatter(_log_formatter)
- log.addHandler(_log_handler)
- return log
-
+ # this should really go to a file, but file-logging is only
+ # hooked up in parallel applications
+ _log_handler = logging.StreamHandler(open(os.devnull, 'w'))
+ else:
+ _log_handler = logging.StreamHandler()
+ _log_formatter = self._log_formatter_cls(fmt=self.log_format, datefmt=self.log_datefmt)
+ _log_handler.setFormatter(_log_formatter)
+ log.addHandler(_log_handler)
+ return log
+
#: the alias map for configurables
#: Keys might strings or tuples for additional options; single-letter alias accessed like `-v`.
#: Values might be like "Class.trait" strings of two-tuples: (Class.trait, help-text).
aliases = {'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 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 = {
'debug': ({
'Application': {
@@ -266,22 +266,22 @@ class Application(SingletonConfigurable):
},
}, "Show the application's configuration (json format)"),
}
-
- # subcommands for launching other applications
- # if this is not empty, this will be a parent Application
- # this must be a dict of two-tuples,
- # the first element being the application class/import string
- # and the second being the help string for the subcommand
- subcommands = Dict()
- # parse_command_line will initialize a subapp, if requested
- subapp = Instance('traitlets.config.application.Application', allow_none=True)
-
- # extra command-line arguments that don't set config values
- extra_args = List(Unicode())
-
+
+ # subcommands for launching other applications
+ # if this is not empty, this will be a parent Application
+ # this must be a dict of two-tuples,
+ # the first element being the application class/import string
+ # and the second being the help string for the subcommand
+ subcommands = Dict()
+ # parse_command_line will initialize a subapp, if requested
+ subapp = Instance('traitlets.config.application.Application', allow_none=True)
+
+ # extra command-line arguments that don't set config values
+ extra_args = List(Unicode())
+
cli_config = Instance(Config, (), {},
help="""The subset of our configuration that came from the command-line
-
+
We re-load this configuration after loading config files,
to ensure that it maintains highest priority.
"""
@@ -307,10 +307,10 @@ class Application(SingletonConfigurable):
self._save_start = self.start
self.start = self.start_show_config
- def __init__(self, **kwargs):
- SingletonConfigurable.__init__(self, **kwargs)
- # Ensure my class is in self.classes, so my attributes appear in command line
- # options and config files.
+ def __init__(self, **kwargs):
+ SingletonConfigurable.__init__(self, **kwargs)
+ # Ensure my class is in self.classes, so my attributes appear in command line
+ # options and config files.
cls = self.__class__
if cls not in self.classes:
if self.classes is cls.classes:
@@ -319,29 +319,29 @@ class Application(SingletonConfigurable):
else:
self.classes.insert(0, self.__class__)
- @observe('config')
- @observe_compat
- def _config_changed(self, change):
- super(Application, self)._config_changed(change)
+ @observe('config')
+ @observe_compat
+ def _config_changed(self, change):
+ super(Application, self)._config_changed(change)
self.log.debug('Config changed: %r', change.new)
-
- @catch_config_error
- def initialize(self, argv=None):
- """Do the basic steps to configure me.
-
- Override in subclasses.
- """
- self.parse_command_line(argv)
-
-
- def start(self):
- """Start the app mainloop.
-
- Override in subclasses.
- """
- if self.subapp is not None:
- return self.subapp.start()
-
+
+ @catch_config_error
+ def initialize(self, argv=None):
+ """Do the basic steps to configure me.
+
+ Override in subclasses.
+ """
+ self.parse_command_line(argv)
+
+
+ def start(self):
+ """Start the app mainloop.
+
+ Override in subclasses.
+ """
+ if self.subapp is not None:
+ return self.subapp.start()
+
def start_show_config(self):
"""start function used when show_config is True"""
config = self.config.copy()
@@ -379,21 +379,21 @@ class Application(SingletonConfigurable):
pprint.pformat(value, **pformat_kwargs),
))
- def print_alias_help(self):
+ def print_alias_help(self):
"""Print the alias parts of the help."""
print('\n'.join(self.emit_alias_help()))
def emit_alias_help(self):
"""Yield the lines for alias part of the help."""
- if not self.aliases:
- return
-
- classdict = {}
- for cls in self.classes:
- # include all parents (up to, but excluding Configurable) in available names
- for c in cls.mro()[:-3]:
- classdict[c.__name__] = c
-
+ if not self.aliases:
+ return
+
+ classdict = {}
+ for cls in self.classes:
+ # include all parents (up to, but excluding Configurable) in available names
+ for c in cls.mro()[:-3]:
+ classdict[c.__name__] = c
+
for alias, longname in self.aliases.items():
try:
if isinstance(longname, tuple):
@@ -403,10 +403,10 @@ class Application(SingletonConfigurable):
classname, traitname = longname.split('.')[-2:]
longname = classname + '.' + traitname
cls = classdict[classname]
-
+
trait = cls.class_traits(config=True)[traitname]
fhelp = cls.class_get_trait_help(trait, helptext=fhelp).splitlines()
-
+
if not isinstance(alias, tuple):
alias = (alias, )
alias = sorted(alias, key=len)
@@ -423,15 +423,15 @@ class Application(SingletonConfigurable):
alias, ex)
raise
- def print_flag_help(self):
- """Print the flag part of the help."""
+ def print_flag_help(self):
+ """Print the flag part of the help."""
print('\n'.join(self.emit_flag_help()))
def emit_flag_help(self):
"""Yield the lines for the flag part of the help."""
- if not self.flags:
- return
-
+ if not self.flags:
+ return
+
for flags, (cfg, fhelp) in self.flags.items():
try:
if not isinstance(flags, tuple):
@@ -451,50 +451,50 @@ class Application(SingletonConfigurable):
self.log.error('Failed collecting help-message for flag %r, due to: %s',
flags, ex)
raise
-
- def print_options(self):
+
+ def print_options(self):
"""Print the options part of the help."""
print('\n'.join(self.emit_options_help()))
def emit_options_help(self):
"""Yield the lines for the options part of the help."""
- if not self.flags and not self.aliases:
- return
+ if not self.flags and not self.aliases:
+ return
header = 'Options'
yield header
yield '=' * len(header)
- for p in wrap_paragraphs(self.option_description):
+ for p in wrap_paragraphs(self.option_description):
yield p
yield ''
-
+
for l in self.emit_flag_help():
yield l
for l in self.emit_alias_help():
yield l
yield ''
- def print_subcommands(self):
- """Print the subcommand part of the help."""
+ def print_subcommands(self):
+ """Print the subcommand part of the help."""
print('\n'.join(self.emit_subcommands_help()))
def emit_subcommands_help(self):
"""Yield the lines for the subcommand part of the help."""
- if not self.subcommands:
- return
-
+ if not self.subcommands:
+ return
+
header = "Subcommands"
yield header
yield '=' * len(header)
- for p in wrap_paragraphs(self.subcommand_description.format(
- app=self.name)):
+ for p in wrap_paragraphs(self.subcommand_description.format(
+ app=self.name)):
yield p
yield ''
for subc, (cls, help) in self.subcommands.items():
yield subc
- if help:
+ if help:
yield indent(dedent(help.strip()))
yield ''
-
+
def emit_help_epilogue(self, classes):
"""Yield the very bottom lines of the help message.
@@ -504,13 +504,13 @@ class Application(SingletonConfigurable):
yield "To see all available configurables, use `--help-all`."
yield ''
- def print_help(self, classes=False):
- """Print the help for each Configurable class in self.classes.
-
- If classes=False (the default), only flags and aliases are printed.
- """
+ def print_help(self, classes=False):
+ """Print the help for each Configurable class in self.classes.
+
+ If classes=False (the default), only flags and aliases are printed.
+ """
print('\n'.join(self.emit_help(classes=classes)))
-
+
def emit_help(self, classes=False):
"""Yield the help-lines for each Configurable class in self.classes.
@@ -523,71 +523,71 @@ class Application(SingletonConfigurable):
for l in self.emit_options_help():
yield l
- if classes:
+ if classes:
help_classes = self._classes_with_config_traits()
- if help_classes:
+ if help_classes:
yield "Class options"
yield "============="
- for p in wrap_paragraphs(self.keyvalue_description):
+ for p in wrap_paragraphs(self.keyvalue_description):
yield p
yield ''
-
- for cls in help_classes:
+
+ for cls in help_classes:
yield cls.class_get_help()
yield ''
for l in self.emit_examples():
yield l
-
+
for l in self.emit_help_epilogue(classes):
yield l
-
- def document_config_options(self):
- """Generate rST format documentation for the config options this application
-
- Returns a multiline string.
- """
- return '\n'.join(c.class_config_rst_doc()
- for c in self._classes_inc_parents())
-
- def print_description(self):
- """Print the application description."""
+
+ def document_config_options(self):
+ """Generate rST format documentation for the config options this application
+
+ Returns a multiline string.
+ """
+ return '\n'.join(c.class_config_rst_doc()
+ for c in self._classes_inc_parents())
+
+ def print_description(self):
+ """Print the application description."""
print('\n'.join(self.emit_description()))
-
+
def emit_description(self):
"""Yield lines with the application description."""
for p in wrap_paragraphs(self.description or self.__doc__):
yield p
yield ''
- def print_examples(self):
+ def print_examples(self):
"""Print usage and examples (see `emit_examples()`). """
print('\n'.join(self.emit_examples()))
-
+
def emit_examples(self):
"""Yield lines with the usage and examples.
- This usage string goes at the end of the command line help string
- and should contain examples of the application's usage.
- """
- if self.examples:
+ This usage string goes at the end of the command line help string
+ and should contain examples of the application's usage.
+ """
+ if self.examples:
yield "Examples"
yield "--------"
yield ''
yield indent(dedent(self.examples.strip()))
yield ''
-
- def print_version(self):
- """Print the version string."""
- print(self.version)
-
- @catch_config_error
- def initialize_subcommand(self, subc, argv=None):
- """Initialize a subcommand with argv."""
+
+ def print_version(self):
+ """Print the version string."""
+ print(self.version)
+
+ @catch_config_error
+ def initialize_subcommand(self, subc, argv=None):
+ """Initialize a subcommand with argv."""
subapp, _ = self.subcommands.get(subc)
-
+
if isinstance(subapp, str):
- subapp = import_item(subapp)
-
+ subapp = import_item(subapp)
+
## Cannot issubclass() on a non-type (SOhttp://stackoverflow.com/questions/8692430)
if isinstance(subapp, type) and issubclass(subapp, Application):
# Clear existing instances before...
@@ -601,55 +601,55 @@ class Application(SingletonConfigurable):
raise AssertionError("Invalid mappings for subcommand '%s'!" % subc)
# ... and finally initialize subapp.
- self.subapp.initialize(argv)
+ self.subapp.initialize(argv)
- def flatten_flags(self):
+ def flatten_flags(self):
"""Flatten flags and aliases for loaders, so cl-args override as expected.
- This prevents issues such as an alias pointing to InteractiveShell,
- but a config file setting the same trait in TerminalInteraciveShell
- getting inappropriate priority over the command-line arg.
+ This prevents issues such as an alias pointing to InteractiveShell,
+ but a config file setting the same trait in TerminalInteraciveShell
+ getting inappropriate priority over the command-line arg.
Also, loaders expect ``(key: longname)`` and not ````key: (longname, help)`` items.
-
- Only aliases with exactly one descendent in the class list
- will be promoted.
-
- """
- # build a tree of classes in our list that inherit from a particular
- # it will be a dict by parent classname of classes in our list
- # that are descendents
- mro_tree = defaultdict(list)
- for cls in self.classes:
- clsname = cls.__name__
- for parent in cls.mro()[1:-3]:
- # exclude cls itself and Configurable,HasTraits,object
- mro_tree[parent.__name__].append(clsname)
- # flatten aliases, which have the form:
- # { 'alias' : 'Class.trait' }
- aliases = {}
+
+ Only aliases with exactly one descendent in the class list
+ will be promoted.
+
+ """
+ # build a tree of classes in our list that inherit from a particular
+ # it will be a dict by parent classname of classes in our list
+ # that are descendents
+ mro_tree = defaultdict(list)
+ for cls in self.classes:
+ clsname = cls.__name__
+ for parent in cls.mro()[1:-3]:
+ # exclude cls itself and Configurable,HasTraits,object
+ mro_tree[parent.__name__].append(clsname)
+ # flatten aliases, which have the form:
+ # { 'alias' : 'Class.trait' }
+ aliases = {}
for alias, longname in self.aliases.items():
if isinstance(longname, tuple):
longname, _ = longname
cls, trait = longname.split('.', 1)
- children = mro_tree[cls]
- if len(children) == 1:
- # exactly one descendent, promote alias
- cls = children[0]
+ children = mro_tree[cls]
+ if len(children) == 1:
+ # exactly one descendent, promote alias
+ cls = children[0]
if not isinstance(aliases, tuple):
alias = (alias, )
for al in alias:
aliases[al] = '.'.join([cls,trait])
- # flatten flags, which are of the form:
- # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
- flags = {}
+ # flatten flags, which are of the form:
+ # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
+ flags = {}
for key, (flagdict, help) in self.flags.items():
- newflag = {}
+ newflag = {}
for cls, subdict in flagdict.items():
- children = mro_tree[cls]
- # exactly one descendent, promote flag section
- if len(children) == 1:
- cls = children[0]
+ children = mro_tree[cls]
+ # exactly one descendent, promote flag section
+ if len(children) == 1:
+ cls = children[0]
if cls in newflag:
newflag[cls].update(subdict)
@@ -660,48 +660,48 @@ class Application(SingletonConfigurable):
key = (key, )
for k in key:
flags[k] = (newflag, help)
- return flags, aliases
-
+ return flags, aliases
+
def _create_loader(self, argv, aliases, flags, classes):
return KVArgParseConfigLoader(argv, aliases, flags, classes=classes,
log=self.log)
- @catch_config_error
- def parse_command_line(self, argv=None):
- """Parse the command line arguments."""
+ @catch_config_error
+ def parse_command_line(self, argv=None):
+ """Parse the command line arguments."""
assert not isinstance(argv, str)
- argv = sys.argv[1:] if argv is None else argv
+ argv = sys.argv[1:] if argv is None else argv
self.argv = [cast_unicode(arg) for arg in argv ]
- if argv and argv[0] == 'help':
- # turn `ipython help notebook` into `ipython notebook -h`
- argv = argv[1:] + ['-h']
-
- if self.subcommands and len(argv) > 0:
- # we have subcommands, and one may have been specified
- subc, subargv = argv[0], argv[1:]
- if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
- # it's a subcommand, and *not* a flag or class parameter
- return self.initialize_subcommand(subc, subargv)
-
- # Arguments after a '--' argument are for the script IPython may be
- # about to run, not IPython iteslf. For arguments parsed here (help and
- # version), we want to only search the arguments up to the first
- # occurrence of '--', which we're calling interpreted_argv.
- try:
- interpreted_argv = argv[:argv.index('--')]
- except ValueError:
- interpreted_argv = argv
-
- if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
- self.print_help('--help-all' in interpreted_argv)
- self.exit(0)
-
- if '--version' in interpreted_argv or '-V' in interpreted_argv:
- self.print_version()
- self.exit(0)
-
- # flatten flags&aliases, so cl-args get appropriate priority:
+ if argv and argv[0] == 'help':
+ # turn `ipython help notebook` into `ipython notebook -h`
+ argv = argv[1:] + ['-h']
+
+ if self.subcommands and len(argv) > 0:
+ # we have subcommands, and one may have been specified
+ subc, subargv = argv[0], argv[1:]
+ if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
+ # it's a subcommand, and *not* a flag or class parameter
+ return self.initialize_subcommand(subc, subargv)
+
+ # Arguments after a '--' argument are for the script IPython may be
+ # about to run, not IPython iteslf. For arguments parsed here (help and
+ # version), we want to only search the arguments up to the first
+ # occurrence of '--', which we're calling interpreted_argv.
+ try:
+ interpreted_argv = argv[:argv.index('--')]
+ except ValueError:
+ interpreted_argv = argv
+
+ if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')):
+ self.print_help('--help-all' in interpreted_argv)
+ self.exit(0)
+
+ if '--version' in interpreted_argv or '-V' in interpreted_argv:
+ self.print_version()
+ self.exit(0)
+
+ # flatten flags&aliases, so cl-args get appropriate priority:
flags, aliases = self.flatten_flags()
classes = tuple(self._classes_with_config_traits())
loader = self._create_loader(argv, aliases, flags, classes=classes)
@@ -712,46 +712,46 @@ class Application(SingletonConfigurable):
# help output is huge, and comes after the error
raise
self.update_config(self.cli_config)
- # store unparsed args in extra_args
- self.extra_args = loader.extra_args
-
- @classmethod
+ # store unparsed args in extra_args
+ self.extra_args = loader.extra_args
+
+ @classmethod
def _load_config_files(cls, basefilename, path=None, log=None, raise_config_file_errors=False):
- """Load config files (py,json) by filename and path.
-
- yield each config object in turn.
- """
-
- if not isinstance(path, list):
- path = [path]
- for path in path[::-1]:
- # path list is in descending priority order, so load files backwards:
- pyloader = cls.python_config_loader_class(basefilename+'.py', path=path, log=log)
- if log:
+ """Load config files (py,json) by filename and path.
+
+ yield each config object in turn.
+ """
+
+ if not isinstance(path, list):
+ path = [path]
+ for path in path[::-1]:
+ # path list is in descending priority order, so load files backwards:
+ pyloader = cls.python_config_loader_class(basefilename+'.py', path=path, log=log)
+ if log:
log.debug("Looking for %s in %s", basefilename, path or os.getcwd())
- jsonloader = cls.json_config_loader_class(basefilename+'.json', path=path, log=log)
+ jsonloader = cls.json_config_loader_class(basefilename+'.json', path=path, log=log)
loaded = []
filenames = []
- for loader in [pyloader, jsonloader]:
+ for loader in [pyloader, jsonloader]:
config = None
- try:
- config = loader.load_config()
- except ConfigFileNotFound:
- pass
- except Exception:
- # try to get the full filename, but it will be empty in the
- # unlikely event that the error raised before filefind finished
- filename = loader.full_filename or basefilename
- # problem while running the file
+ try:
+ config = loader.load_config()
+ except ConfigFileNotFound:
+ pass
+ except Exception:
+ # try to get the full filename, but it will be empty in the
+ # unlikely event that the error raised before filefind finished
+ filename = loader.full_filename or basefilename
+ # problem while running the file
if raise_config_file_errors:
raise
- if log:
- log.error("Exception while loading config file %s",
- filename, exc_info=True)
- else:
- if log:
- log.debug("Loaded config file: %s", loader.full_filename)
- if config:
+ if log:
+ log.error("Exception while loading config file %s",
+ filename, exc_info=True)
+ else:
+ if log:
+ log.debug("Loaded config file: %s", loader.full_filename)
+ if config:
for filename, earlier_config in zip(filenames, loaded):
collisions = earlier_config.collisions(config)
if collisions and log:
@@ -762,16 +762,16 @@ class Application(SingletonConfigurable):
yield (config, loader.full_filename)
loaded.append(config)
filenames.append(loader.full_filename)
-
+
@property
def loaded_config_files(self):
"""Currently loaded configuration files"""
return self._loaded_config_files[:]
-
- @catch_config_error
- def load_config_file(self, filename, path=None):
- """Load config files by filename and path."""
- filename, ext = os.path.splitext(filename)
+
+ @catch_config_error
+ def load_config_file(self, filename, path=None):
+ """Load config files by filename and path."""
+ filename, ext = os.path.splitext(filename)
new_config = Config()
for (config, filename) in self._load_config_files(filename, path=path, log=self.log,
raise_config_file_errors=self.raise_config_file_errors,
@@ -782,7 +782,7 @@ class Application(SingletonConfigurable):
# add self.cli_config to preserve CLI config priority
new_config.merge(self.cli_config)
self.update_config(new_config)
-
+
def _classes_with_config_traits(self, classes=None):
"""
Yields only classes with configurable traits, and their subclasses.
@@ -822,76 +822,76 @@ class Application(SingletonConfigurable):
yield cl
def generate_config_file(self, classes=None):
- """generate default config file from Configurables"""
- lines = ["# Configuration file for %s." % self.name]
- lines.append('')
+ """generate default config file from Configurables"""
+ lines = ["# Configuration file for %s." % self.name]
+ lines.append('')
classes = self.classes if classes is None else classes
config_classes = list(self._classes_with_config_traits(classes))
for cls in config_classes:
lines.append(cls.class_config_section(config_classes))
- return '\n'.join(lines)
-
- def exit(self, exit_status=0):
- self.log.debug("Exiting application: %s" % self.name)
- sys.exit(exit_status)
-
- @classmethod
- def launch_instance(cls, argv=None, **kwargs):
- """Launch a global instance of this Application
-
- If a global instance already exists, this reinitializes and starts it
- """
- app = cls.instance(**kwargs)
- app.initialize(argv)
- app.start()
-
-#-----------------------------------------------------------------------------
-# utility functions, for convenience
-#-----------------------------------------------------------------------------
-
+ return '\n'.join(lines)
+
+ def exit(self, exit_status=0):
+ self.log.debug("Exiting application: %s" % self.name)
+ sys.exit(exit_status)
+
+ @classmethod
+ def launch_instance(cls, argv=None, **kwargs):
+ """Launch a global instance of this Application
+
+ If a global instance already exists, this reinitializes and starts it
+ """
+ app = cls.instance(**kwargs)
+ app.initialize(argv)
+ app.start()
+
+#-----------------------------------------------------------------------------
+# utility functions, for convenience
+#-----------------------------------------------------------------------------
+
default_aliases = Application.aliases
default_flags = Application.flags
-def boolean_flag(name, configurable, set_help='', unset_help=''):
- """Helper for building basic --trait, --no-trait flags.
-
- Parameters
- ----------
- name : str
- The name of the flag.
- configurable : str
- The 'Class.trait' string of the trait to be set/unset with the flag
- set_help : unicode
- help string for --name flag
- unset_help : unicode
- help string for --no-name flag
-
- Returns
- -------
- cfg : dict
- A dict with two keys: 'name', and 'no-name', for setting and unsetting
- the trait, respectively.
- """
- # default helpstrings
- set_help = set_help or "set %s=True"%configurable
- unset_help = unset_help or "set %s=False"%configurable
-
- cls,trait = configurable.split('.')
-
- setter = {cls : {trait : True}}
- unsetter = {cls : {trait : False}}
- return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
-
-
-def get_config():
- """Get the config object for the global Application instance, if there is one
-
- otherwise return an empty config object
- """
- if Application.initialized():
- return Application.instance().config
- else:
- return Config()
+def boolean_flag(name, configurable, set_help='', unset_help=''):
+ """Helper for building basic --trait, --no-trait flags.
+
+ Parameters
+ ----------
+ name : str
+ The name of the flag.
+ configurable : str
+ The 'Class.trait' string of the trait to be set/unset with the flag
+ set_help : unicode
+ help string for --name flag
+ unset_help : unicode
+ help string for --no-name flag
+
+ Returns
+ -------
+ cfg : dict
+ A dict with two keys: 'name', and 'no-name', for setting and unsetting
+ the trait, respectively.
+ """
+ # default helpstrings
+ set_help = set_help or "set %s=True"%configurable
+ unset_help = unset_help or "set %s=False"%configurable
+
+ cls,trait = configurable.split('.')
+
+ setter = {cls : {trait : True}}
+ unsetter = {cls : {trait : False}}
+ return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
+
+
+def get_config():
+ """Get the config object for the global Application instance, if there is one
+
+ otherwise return an empty config object
+ """
+ if Application.initialized():
+ return Application.instance().config
+ else:
+ return Config()
if __name__ == '__main__':
diff --git a/contrib/python/traitlets/py3/traitlets/config/configurable.py b/contrib/python/traitlets/py3/traitlets/config/configurable.py
index 3b2044a01b..d6dd93e114 100644
--- a/contrib/python/traitlets/py3/traitlets/config/configurable.py
+++ b/contrib/python/traitlets/py3/traitlets/config/configurable.py
@@ -1,13 +1,13 @@
-"""A base class for objects that are configurable."""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-
-from copy import deepcopy
+"""A base class for objects that are configurable."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+
+from copy import deepcopy
import logging
import warnings
-
+
from .loader import Config, LazyConfigValue, DeferredConfig, _is_section_key
from traitlets.traitlets import (
Any,
@@ -22,67 +22,67 @@ from traitlets.traitlets import (
)
from traitlets.utils.text import indent, wrap_paragraphs
from textwrap import dedent
-
-
-
-
-#-----------------------------------------------------------------------------
-# Helper classes for Configurables
-#-----------------------------------------------------------------------------
-
-
-class ConfigurableError(Exception):
- pass
-
-
-class MultipleInstanceError(ConfigurableError):
- pass
-
-#-----------------------------------------------------------------------------
-# Configurable implementation
-#-----------------------------------------------------------------------------
-
-class Configurable(HasTraits):
-
- config = Instance(Config, (), {})
- parent = Instance('traitlets.config.configurable.Configurable', allow_none=True)
-
- def __init__(self, **kwargs):
- """Create a configurable given a config config.
-
- Parameters
- ----------
- config : Config
- If this is empty, default values are used. If config is a
- :class:`Config` instance, it will be used to configure the
- instance.
- parent : Configurable instance, optional
- The parent Configurable instance of this object.
-
- Notes
- -----
- Subclasses of Configurable must call the :meth:`__init__` method of
- :class:`Configurable` *before* doing anything else and using
- :func:`super`::
-
- class MyConfigurable(Configurable):
- def __init__(self, config=None):
- super(MyConfigurable, self).__init__(config=config)
- # Then any other code you need to finish initialization.
-
- This ensures that instances will be configured properly.
- """
- parent = kwargs.pop('parent', None)
- if parent is not None:
- # config is implied from parent
- if kwargs.get('config', None) is None:
- kwargs['config'] = parent.config
- self.parent = parent
-
- config = kwargs.pop('config', None)
-
- # load kwarg traits, other than config
- super(Configurable, self).__init__(**kwargs)
+
+
+
+
+#-----------------------------------------------------------------------------
+# Helper classes for Configurables
+#-----------------------------------------------------------------------------
+
+
+class ConfigurableError(Exception):
+ pass
+
+
+class MultipleInstanceError(ConfigurableError):
+ pass
+
+#-----------------------------------------------------------------------------
+# Configurable implementation
+#-----------------------------------------------------------------------------
+
+class Configurable(HasTraits):
+
+ config = Instance(Config, (), {})
+ parent = Instance('traitlets.config.configurable.Configurable', allow_none=True)
+
+ def __init__(self, **kwargs):
+ """Create a configurable given a config config.
+
+ Parameters
+ ----------
+ config : Config
+ If this is empty, default values are used. If config is a
+ :class:`Config` instance, it will be used to configure the
+ instance.
+ parent : Configurable instance, optional
+ The parent Configurable instance of this object.
+
+ Notes
+ -----
+ Subclasses of Configurable must call the :meth:`__init__` method of
+ :class:`Configurable` *before* doing anything else and using
+ :func:`super`::
+
+ class MyConfigurable(Configurable):
+ def __init__(self, config=None):
+ super(MyConfigurable, self).__init__(config=config)
+ # Then any other code you need to finish initialization.
+
+ This ensures that instances will be configured properly.
+ """
+ parent = kwargs.pop('parent', None)
+ if parent is not None:
+ # config is implied from parent
+ if kwargs.get('config', None) is None:
+ kwargs['config'] = parent.config
+ self.parent = parent
+
+ config = kwargs.pop('config', None)
+
+ # load kwarg traits, other than config
+ super(Configurable, self).__init__(**kwargs)
# record traits set by config
config_override_names = set()
@@ -95,122 +95,122 @@ class Configurable(HasTraits):
config_override_names.add(change.name)
self.observe(notice_config_override)
- # load config
- if config is not None:
- # We used to deepcopy, but for now we are trying to just save
- # by reference. This *could* have side effects as all components
- # will share config. In fact, I did find such a side effect in
- # _config_changed below. If a config attribute value was a mutable type
- # all instances of a component were getting the same copy, effectively
- # making that a class attribute.
- # self.config = deepcopy(config)
- self.config = config
- else:
- # allow _config_default to return something
- self._load_config(self.config)
+ # load config
+ if config is not None:
+ # We used to deepcopy, but for now we are trying to just save
+ # by reference. This *could* have side effects as all components
+ # will share config. In fact, I did find such a side effect in
+ # _config_changed below. If a config attribute value was a mutable type
+ # all instances of a component were getting the same copy, effectively
+ # making that a class attribute.
+ # self.config = deepcopy(config)
+ self.config = config
+ else:
+ # allow _config_default to return something
+ self._load_config(self.config)
self.unobserve(notice_config_override)
for name in config_override_names:
setattr(self, name, kwargs[name])
+
+ #-------------------------------------------------------------------------
+ # Static trait notifiations
+ #-------------------------------------------------------------------------
- #-------------------------------------------------------------------------
- # Static trait notifiations
- #-------------------------------------------------------------------------
-
- @classmethod
- def section_names(cls):
- """return section names as a list"""
- return [c.__name__ for c in reversed(cls.__mro__) if
- issubclass(c, Configurable) and issubclass(cls, c)
- ]
+ @classmethod
+ def section_names(cls):
+ """return section names as a list"""
+ return [c.__name__ for c in reversed(cls.__mro__) if
+ issubclass(c, Configurable) and issubclass(cls, c)
+ ]
- def _find_my_config(self, cfg):
- """extract my config from a global Config object
+ def _find_my_config(self, cfg):
+ """extract my config from a global Config object
- will construct a Config object of only the config values that apply to me
- based on my mro(), as well as those of my parent(s) if they exist.
+ will construct a Config object of only the config values that apply to me
+ based on my mro(), as well as those of my parent(s) if they exist.
- If I am Bar and my parent is Foo, and their parent is Tim,
- this will return merge following config sections, in this order::
+ If I am Bar and my parent is Foo, and their parent is Tim,
+ this will return merge following config sections, in this order::
[Bar, Foo.Bar, Tim.Foo.Bar]
- With the last item being the highest priority.
- """
- cfgs = [cfg]
- if self.parent:
- cfgs.append(self.parent._find_my_config(cfg))
- my_config = Config()
- for c in cfgs:
- for sname in self.section_names():
- # Don't do a blind getattr as that would cause the config to
- # dynamically create the section with name Class.__name__.
- if c._has_section(sname):
- my_config.merge(c[sname])
- return my_config
-
- def _load_config(self, cfg, section_names=None, traits=None):
- """load traits from a Config object"""
-
- if traits is None:
- traits = self.traits(config=True)
- if section_names is None:
- section_names = self.section_names()
-
- my_config = self._find_my_config(cfg)
-
- # hold trait notifications until after all config has been loaded
- with self.hold_trait_notifications():
+ With the last item being the highest priority.
+ """
+ cfgs = [cfg]
+ if self.parent:
+ cfgs.append(self.parent._find_my_config(cfg))
+ my_config = Config()
+ for c in cfgs:
+ for sname in self.section_names():
+ # Don't do a blind getattr as that would cause the config to
+ # dynamically create the section with name Class.__name__.
+ if c._has_section(sname):
+ my_config.merge(c[sname])
+ return my_config
+
+ def _load_config(self, cfg, section_names=None, traits=None):
+ """load traits from a Config object"""
+
+ if traits is None:
+ traits = self.traits(config=True)
+ if section_names is None:
+ section_names = self.section_names()
+
+ my_config = self._find_my_config(cfg)
+
+ # hold trait notifications until after all config has been loaded
+ with self.hold_trait_notifications():
for name, config_value in my_config.items():
- if name in traits:
- if isinstance(config_value, LazyConfigValue):
- # ConfigValue is a wrapper for using append / update on containers
- # without having to copy the initial value
- initial = getattr(self, name)
- config_value = config_value.get_value(initial)
+ if name in traits:
+ if isinstance(config_value, LazyConfigValue):
+ # ConfigValue is a wrapper for using append / update on containers
+ # without having to copy the initial value
+ initial = getattr(self, name)
+ config_value = config_value.get_value(initial)
elif isinstance(config_value, DeferredConfig):
# DeferredConfig tends to come from CLI/environment variables
config_value = config_value.get_value(traits[name])
- # We have to do a deepcopy here if we don't deepcopy the entire
- # config object. If we don't, a mutable config_value will be
- # shared by all instances, effectively making it a class attribute.
- setattr(self, name, deepcopy(config_value))
+ # We have to do a deepcopy here if we don't deepcopy the entire
+ # config object. If we don't, a mutable config_value will be
+ # shared by all instances, effectively making it a class attribute.
+ setattr(self, name, deepcopy(config_value))
elif not _is_section_key(name) and not isinstance(config_value, Config):
- from difflib import get_close_matches
+ from difflib import get_close_matches
if isinstance(self, LoggingConfigurable):
warn = self.log.warning
else:
warn = lambda msg: warnings.warn(msg, stacklevel=9)
- matches = get_close_matches(name, traits)
+ matches = get_close_matches(name, traits)
msg = "Config option `{option}` not recognized by `{klass}`.".format(
option=name, klass=self.__class__.__name__)
- if len(matches) == 1:
+ if len(matches) == 1:
msg += " Did you mean `{matches}`?".format(matches=matches[0])
- elif len(matches) >= 1:
+ elif len(matches) >= 1:
msg +=" Did you mean one of: `{matches}`?".format(matches=', '.join(sorted(matches)))
warn(msg)
-
- @observe('config')
- @observe_compat
- def _config_changed(self, change):
- """Update all the class traits having ``config=True`` in metadata.
-
- For any class trait with a ``config`` metadata attribute that is
- ``True``, we update the trait with the value of the corresponding
- config entry.
- """
- # Get all traits with a config metadata entry that is True
- traits = self.traits(config=True)
-
- # We auto-load config section for this class as well as any parent
- # classes that are Configurable subclasses. This starts with Configurable
- # and works down the mro loading the config for each section.
- section_names = self.section_names()
+
+ @observe('config')
+ @observe_compat
+ def _config_changed(self, change):
+ """Update all the class traits having ``config=True`` in metadata.
+
+ For any class trait with a ``config`` metadata attribute that is
+ ``True``, we update the trait with the value of the corresponding
+ config entry.
+ """
+ # Get all traits with a config metadata entry that is True
+ traits = self.traits(config=True)
+
+ # We auto-load config section for this class as well as any parent
+ # classes that are Configurable subclasses. This starts with Configurable
+ # and works down the mro loading the config for each section.
+ section_names = self.section_names()
self._load_config(change.new, traits=traits, section_names=section_names)
-
- def update_config(self, config):
+
+ def update_config(self, config):
"""Update config and load the new values"""
# traitlets prior to 4.2 created a copy of self.config in order to trigger change events.
# Some projects (IPython < 5) relied upon one side effect of this,
@@ -222,28 +222,28 @@ class Configurable(HasTraits):
# load config
self._load_config(config)
# merge it into self.config
- self.config.merge(config)
+ self.config.merge(config)
# TODO: trigger change event if/when dict-update change events take place
# DO NOT trigger full trait-change
-
- @classmethod
- def class_get_help(cls, inst=None):
- """Get the help string for this class in ReST format.
-
- If `inst` is given, it's current trait values will be used in place of
- class defaults.
- """
- assert inst is None or isinstance(inst, cls)
- final_help = []
+
+ @classmethod
+ def class_get_help(cls, inst=None):
+ """Get the help string for this class in ReST format.
+
+ If `inst` is given, it's current trait values will be used in place of
+ class defaults.
+ """
+ assert inst is None or isinstance(inst, cls)
+ final_help = []
base_classes = ', '.join(p.__name__ for p in cls.__bases__)
final_help.append('%s(%s) options' % (cls.__name__, base_classes))
final_help.append(len(final_help[0])*'-')
- for k, v in sorted(cls.class_traits(config=True).items()):
- help = cls.class_get_trait_help(v, inst)
- final_help.append(help)
- return '\n'.join(final_help)
-
- @classmethod
+ for k, v in sorted(cls.class_traits(config=True).items()):
+ help = cls.class_get_trait_help(v, inst)
+ final_help.append(help)
+ return '\n'.join(final_help)
+
+ @classmethod
def class_get_trait_help(cls, trait, inst=None, helptext=None):
"""Get the helptext string for a single trait.
@@ -252,9 +252,9 @@ class Configurable(HasTraits):
the class default.
:param helptext:
If not given, uses the `help` attribute of the current trait.
- """
- assert inst is None or isinstance(inst, cls)
- lines = []
+ """
+ assert inst is None or isinstance(inst, cls)
+ lines = []
header = "--%s.%s" % (cls.__name__, trait.name)
if isinstance(trait, (Container, Dict)):
multiplicity = trait.metadata.get('multiplicity', 'append')
@@ -269,7 +269,7 @@ class Configurable(HasTraits):
else:
header = '%s=<%s>' % (header, trait.__class__.__name__)
#header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__)
- lines.append(header)
+ lines.append(header)
if helptext is None:
helptext = trait.help
@@ -281,26 +281,26 @@ class Configurable(HasTraits):
# include Enum choices
lines.append(indent('Choices: %s' % trait.info()))
- if inst is not None:
+ if inst is not None:
lines.append(indent("Current: %r" % (getattr(inst, trait.name),)))
- else:
- try:
- dvr = trait.default_value_repr()
- except Exception:
- dvr = None # ignore defaults we can't construct
- if dvr is not None:
- if len(dvr) > 64:
+ else:
+ try:
+ dvr = trait.default_value_repr()
+ except Exception:
+ dvr = None # ignore defaults we can't construct
+ if dvr is not None:
+ if len(dvr) > 64:
dvr = dvr[:61] + "..."
lines.append(indent("Default: %s" % dvr))
-
- return '\n'.join(lines)
-
- @classmethod
- def class_print_help(cls, inst=None):
- """Get the help string for a single trait and print it."""
- print(cls.class_get_help(inst))
-
- @classmethod
+
+ return '\n'.join(lines)
+
+ @classmethod
+ def class_print_help(cls, inst=None):
+ """Get the help string for a single trait and print it."""
+ print(cls.class_get_help(inst))
+
+ @classmethod
def _defining_class(cls, trait, classes):
"""Get the class that defines a trait
@@ -336,13 +336,13 @@ class Configurable(HasTraits):
The list of other classes in the config file.
Used to reduce redundant information.
"""
- def c(s):
- """return a commented, wrapped block."""
- s = '\n\n'.join(wrap_paragraphs(s, 78))
-
+ def c(s):
+ """return a commented, wrapped block."""
+ s = '\n\n'.join(wrap_paragraphs(s, 78))
+
return '## ' + s.replace('\n', '\n# ')
-
- # section header
+
+ # section header
breaker = '#' + '-' * 78
parent_classes = ', '.join(
p.__name__ for p in cls.__bases__
@@ -351,17 +351,17 @@ class Configurable(HasTraits):
s = "# %s(%s) configuration" % (cls.__name__, parent_classes)
lines = [breaker, s, breaker]
- # get the description trait
- desc = cls.class_traits().get('description')
- if desc:
- desc = desc.default_value
+ # get the description trait
+ desc = cls.class_traits().get('description')
+ if desc:
+ desc = desc.default_value
if not desc:
# no description from trait, use __doc__
- desc = getattr(cls, '__doc__', '')
- if desc:
- lines.append(c(desc))
- lines.append('')
-
+ desc = getattr(cls, '__doc__', '')
+ if desc:
+ lines.append(c(desc))
+ lines.append('')
+
for name, trait in sorted(cls.class_traits(config=True).items()):
default_repr = trait.default_value_repr()
@@ -385,60 +385,60 @@ class Configurable(HasTraits):
lines.append('# See also: %s.%s' % (defining_class.__name__, name))
lines.append('# c.%s.%s = %s' % (cls.__name__, name, default_repr))
- lines.append('')
- return '\n'.join(lines)
-
- @classmethod
- def class_config_rst_doc(cls):
- """Generate rST documentation for this class' config options.
-
- Excludes traits defined on parent classes.
- """
- lines = []
- classname = cls.__name__
+ lines.append('')
+ return '\n'.join(lines)
+
+ @classmethod
+ def class_config_rst_doc(cls):
+ """Generate rST documentation for this class' config options.
+
+ Excludes traits defined on parent classes.
+ """
+ lines = []
+ classname = cls.__name__
for k, trait in sorted(cls.class_traits(config=True).items()):
- ttype = trait.__class__.__name__
-
- termline = classname + '.' + trait.name
-
- # Choices or type
- if 'Enum' in ttype:
- # include Enum choices
+ ttype = trait.__class__.__name__
+
+ termline = classname + '.' + trait.name
+
+ # Choices or type
+ if 'Enum' in ttype:
+ # include Enum choices
termline += ' : ' + trait.info_rst()
- else:
- termline += ' : ' + ttype
- lines.append(termline)
-
- # Default value
- try:
- dvr = trait.default_value_repr()
- except Exception:
- dvr = None # ignore defaults we can't construct
- if dvr is not None:
- if len(dvr) > 64:
- dvr = dvr[:61]+'...'
- # Double up backslashes, so they get to the rendered docs
+ else:
+ termline += ' : ' + ttype
+ lines.append(termline)
+
+ # Default value
+ try:
+ dvr = trait.default_value_repr()
+ except Exception:
+ dvr = None # ignore defaults we can't construct
+ if dvr is not None:
+ if len(dvr) > 64:
+ dvr = dvr[:61]+'...'
+ # Double up backslashes, so they get to the rendered docs
dvr = dvr.replace("\\n", "\\\\n")
lines.append(indent("Default: ``%s``" % dvr))
lines.append("")
-
- help = trait.help or 'No description'
+
+ help = trait.help or 'No description'
lines.append(indent(dedent(help)))
-
- # Blank line
- lines.append('')
-
- return '\n'.join(lines)
-
-
-
-class LoggingConfigurable(Configurable):
- """A parent class for Configurables that log.
-
- Subclasses have a log trait, and the default behavior
- is to get the logger from the currently running Application.
- """
-
+
+ # Blank line
+ lines.append('')
+
+ return '\n'.join(lines)
+
+
+
+class LoggingConfigurable(Configurable):
+ """A parent class for Configurables that log.
+
+ Subclasses have a log trait, and the default behavior
+ is to get the logger from the currently running Application.
+ """
+
log = Any(help="Logger or LoggerAdapter instance")
@validate("log")
@@ -452,15 +452,15 @@ class LoggingConfigurable(Configurable):
return proposal.value
@default("log")
- def _log_default(self):
+ def _log_default(self):
if isinstance(self.parent, LoggingConfigurable):
return self.parent.log
- from traitlets import log
- return log.get_logger()
-
+ from traitlets import log
+ return log.get_logger()
+
def _get_log_handler(self):
"""Return the default Handler
-
+
Returns None if none can be found
"""
logger = self.log
@@ -472,89 +472,89 @@ class LoggingConfigurable(Configurable):
return logger.handlers[0]
-class SingletonConfigurable(LoggingConfigurable):
- """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 _walk_mro(cls):
- """Walk the cls.mro() for parent classes that are also singletons
-
- For use in instance()
- """
-
- for subclass in cls.mro():
- if issubclass(cls, subclass) and \
- issubclass(subclass, SingletonConfigurable) and \
- subclass != SingletonConfigurable:
- yield subclass
-
- @classmethod
- def clear_instance(cls):
- """unset _instance for this class and singleton parents.
- """
- if not cls.initialized():
- return
- for subclass in cls._walk_mro():
- if isinstance(subclass._instance, cls):
- # only clear instances that are instances
- # of the calling class
- subclass._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 traitlets.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
- # parent classes' _instance attribute.
- for subclass in cls._walk_mro():
- subclass._instance = inst
-
- if isinstance(cls._instance, cls):
- return cls._instance
- else:
- raise MultipleInstanceError(
+class SingletonConfigurable(LoggingConfigurable):
+ """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 _walk_mro(cls):
+ """Walk the cls.mro() for parent classes that are also singletons
+
+ For use in instance()
+ """
+
+ for subclass in cls.mro():
+ if issubclass(cls, subclass) and \
+ issubclass(subclass, SingletonConfigurable) and \
+ subclass != SingletonConfigurable:
+ yield subclass
+
+ @classmethod
+ def clear_instance(cls):
+ """unset _instance for this class and singleton parents.
+ """
+ if not cls.initialized():
+ return
+ for subclass in cls._walk_mro():
+ if isinstance(subclass._instance, cls):
+ # only clear instances that are instances
+ # of the calling class
+ subclass._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 traitlets.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
+ # parent classes' _instance attribute.
+ for subclass in cls._walk_mro():
+ subclass._instance = inst
+
+ if isinstance(cls._instance, cls):
+ return cls._instance
+ else:
+ raise MultipleInstanceError(
"An incompatible sibling of '%s' is already instanciated"
" as singleton: %s" % (cls.__name__, type(cls._instance).__name__)
- )
-
- @classmethod
- def initialized(cls):
- """Has an instance been created?"""
- return hasattr(cls, "_instance") and cls._instance is not None
-
-
-
+ )
+
+ @classmethod
+ def initialized(cls):
+ """Has an instance been created?"""
+ return hasattr(cls, "_instance") and cls._instance is not None
+
+
+
diff --git a/contrib/python/traitlets/py3/traitlets/config/loader.py b/contrib/python/traitlets/py3/traitlets/config/loader.py
index 5360f889ab..3af27bc22f 100644
--- a/contrib/python/traitlets/py3/traitlets/config/loader.py
+++ b/contrib/python/traitlets/py3/traitlets/config/loader.py
@@ -1,49 +1,49 @@
-"""A simple configuration system."""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-import argparse
-import copy
-import os
-import re
-import sys
-import json
+"""A simple configuration system."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import argparse
+import copy
+import os
+import re
+import sys
+import json
import warnings
-
+
from ..utils import cast_unicode, filefind
from traitlets.traitlets import (
HasTraits, Container, List, Dict, Any, Undefined,
)
-
-#-----------------------------------------------------------------------------
-# Exceptions
-#-----------------------------------------------------------------------------
-
-
-class ConfigError(Exception):
- pass
-
-class ConfigLoaderError(ConfigError):
- pass
-
-class ConfigFileNotFound(ConfigError):
- pass
-
-class ArgumentError(ConfigLoaderError):
- pass
-
-#-----------------------------------------------------------------------------
-# Argparse fix
-#-----------------------------------------------------------------------------
-
-# Unfortunately argparse by default prints help messages to stderr instead of
-# stdout. This makes it annoying to capture long help screens at the command
-# line, since one must know how to pipe stderr, which many users don't know how
-# to do. So we override the print_help method with one that defaults to
-# stdout and use our class instead.
-
+
+#-----------------------------------------------------------------------------
+# Exceptions
+#-----------------------------------------------------------------------------
+
+
+class ConfigError(Exception):
+ pass
+
+class ConfigLoaderError(ConfigError):
+ pass
+
+class ConfigFileNotFound(ConfigError):
+ pass
+
+class ArgumentError(ConfigLoaderError):
+ pass
+
+#-----------------------------------------------------------------------------
+# Argparse fix
+#-----------------------------------------------------------------------------
+
+# Unfortunately argparse by default prints help messages to stderr instead of
+# stdout. This makes it annoying to capture long help screens at the command
+# line, since one must know how to pipe stderr, which many users don't know how
+# to do. So we override the print_help method with one that defaults to
+# stdout and use our class instead.
+
class _Sentinel:
def __repr__(self):
@@ -56,55 +56,55 @@ class _Sentinel:
_deprecated = _Sentinel()
-class ArgumentParser(argparse.ArgumentParser):
- """Simple argparse subclass that prints help to stdout by default."""
-
- def print_help(self, file=None):
- if file is None:
- file = sys.stdout
- return super(ArgumentParser, self).print_help(file)
-
- print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
-
-#-----------------------------------------------------------------------------
-# Config class for holding config information
-#-----------------------------------------------------------------------------
-
+class ArgumentParser(argparse.ArgumentParser):
+ """Simple argparse subclass that prints help to stdout by default."""
+
+ def print_help(self, file=None):
+ if file is None:
+ file = sys.stdout
+ return super(ArgumentParser, self).print_help(file)
+
+ print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
+
+#-----------------------------------------------------------------------------
+# Config class for holding config information
+#-----------------------------------------------------------------------------
+
def execfile(fname, glob):
with open(fname, 'rb') as f:
exec(compile(f.read(), fname, 'exec'), glob, glob)
-class LazyConfigValue(HasTraits):
- """Proxy object for exposing methods on configurable containers
+class LazyConfigValue(HasTraits):
+ """Proxy object for exposing methods on configurable containers
These methods allow appending/extending/updating
to add to non-empty defaults instead of clobbering them.
- Exposes:
+ Exposes:
- - append, extend, insert on lists
- - update on dicts
- - update, add on sets
- """
+ - append, extend, insert on lists
+ - update on dicts
+ - update, add on sets
+ """
- _value = None
+ _value = None
- # list methods
- _extend = List()
- _prepend = List()
+ # list methods
+ _extend = List()
+ _prepend = List()
_inserts = List()
- def append(self, obj):
+ def append(self, obj):
"""Append an item to a List"""
- self._extend.append(obj)
+ self._extend.append(obj)
- def extend(self, other):
+ def extend(self, other):
"""Extend a list"""
- self._extend.extend(other)
+ self._extend.extend(other)
- def prepend(self, other):
- """like list.extend, but for the front"""
- self._prepend[:0] = other
+ def prepend(self, other):
+ """like list.extend, but for the front"""
+ self._prepend[:0] = other
def merge_into(self, other):
@@ -140,86 +140,86 @@ class LazyConfigValue(HasTraits):
# other is a container, reify now.
return self.get_value(other)
- def insert(self, index, other):
- if not isinstance(index, int):
- raise TypeError("An integer is required")
- self._inserts.append((index, other))
+ def insert(self, index, other):
+ if not isinstance(index, int):
+ raise TypeError("An integer is required")
+ self._inserts.append((index, other))
- # dict methods
- # update is used for both dict and set
- _update = Any()
+ # dict methods
+ # update is used for both dict and set
+ _update = Any()
- def update(self, other):
+ def update(self, other):
"""Update either a set or dict"""
- if self._update is None:
- if isinstance(other, dict):
- self._update = {}
- else:
- self._update = set()
- self._update.update(other)
-
- # set methods
- def add(self, obj):
+ if self._update is None:
+ if isinstance(other, dict):
+ self._update = {}
+ else:
+ self._update = set()
+ self._update.update(other)
+
+ # set methods
+ def add(self, obj):
"""Add an item to a set"""
- self.update({obj})
-
- def get_value(self, initial):
- """construct the value from the initial one
-
- after applying any insert / extend / update changes
- """
- if self._value is not None:
- return self._value
- value = copy.deepcopy(initial)
- if isinstance(value, list):
- for idx, obj in self._inserts:
- value.insert(idx, obj)
- value[:0] = self._prepend
- value.extend(self._extend)
-
- elif isinstance(value, dict):
- if self._update:
- value.update(self._update)
- elif isinstance(value, set):
- if self._update:
- value.update(self._update)
- self._value = value
- return value
-
- def to_dict(self):
- """return JSONable dict form of my data
-
- Currently update as dict or set, extend, prepend as lists, and inserts as list of tuples.
- """
- d = {}
- if self._update:
- d['update'] = self._update
- if self._extend:
- d['extend'] = self._extend
- if self._prepend:
- d['prepend'] = self._prepend
- elif self._inserts:
- d['inserts'] = self._inserts
- return d
-
+ self.update({obj})
+
+ def get_value(self, initial):
+ """construct the value from the initial one
+
+ after applying any insert / extend / update changes
+ """
+ if self._value is not None:
+ return self._value
+ value = copy.deepcopy(initial)
+ if isinstance(value, list):
+ for idx, obj in self._inserts:
+ value.insert(idx, obj)
+ value[:0] = self._prepend
+ value.extend(self._extend)
+
+ elif isinstance(value, dict):
+ if self._update:
+ value.update(self._update)
+ elif isinstance(value, set):
+ if self._update:
+ value.update(self._update)
+ self._value = value
+ return value
+
+ def to_dict(self):
+ """return JSONable dict form of my data
+
+ Currently update as dict or set, extend, prepend as lists, and inserts as list of tuples.
+ """
+ d = {}
+ if self._update:
+ d['update'] = self._update
+ if self._extend:
+ d['extend'] = self._extend
+ if self._prepend:
+ d['prepend'] = self._prepend
+ elif self._inserts:
+ d['inserts'] = self._inserts
+ return d
+
def __repr__(self):
if self._value is not None:
return "<%s value=%r>" % (self.__class__.__name__, self._value)
else:
return "<%s %r>" % (self.__class__.__name__, self.to_dict())
-
-
-def _is_section_key(key):
- """Is a Config key a section name (does it start with a capital)?"""
- if key and key[0].upper()==key[0] and not key.startswith('_'):
- return True
- else:
- return False
-
-
-class Config(dict):
+
+
+def _is_section_key(key):
+ """Is a Config key a section name (does it start with a capital)?"""
+ if key and key[0].upper()==key[0] and not key.startswith('_'):
+ return True
+ else:
+ return False
+
+
+class Config(dict):
"""An attribute-based dict that can do smart merges.
-
+
Accessing a field on a config object for the first time populates the key
with either a nested Config object for keys starting with capitals
or :class:`.LazyConfigValue` for lowercase keys,
@@ -231,147 +231,147 @@ class Config(dict):
"""
- def __init__(self, *args, **kwds):
- dict.__init__(self, *args, **kwds)
- self._ensure_subconfig()
-
- def _ensure_subconfig(self):
- """ensure that sub-dicts that should be Config objects are
-
- casts dicts that are under section keys to Config objects,
- which is necessary for constructing Config objects from dict literals.
- """
- for key in self:
- obj = self[key]
- if _is_section_key(key) \
- and isinstance(obj, dict) \
- and not isinstance(obj, Config):
- setattr(self, key, Config(obj))
-
- def _merge(self, other):
- """deprecated alias, use Config.merge()"""
- self.merge(other)
-
- def merge(self, other):
- """merge another config object into this one"""
- to_update = {}
+ def __init__(self, *args, **kwds):
+ dict.__init__(self, *args, **kwds)
+ self._ensure_subconfig()
+
+ def _ensure_subconfig(self):
+ """ensure that sub-dicts that should be Config objects are
+
+ casts dicts that are under section keys to Config objects,
+ which is necessary for constructing Config objects from dict literals.
+ """
+ for key in self:
+ obj = self[key]
+ if _is_section_key(key) \
+ and isinstance(obj, dict) \
+ and not isinstance(obj, Config):
+ setattr(self, key, Config(obj))
+
+ def _merge(self, other):
+ """deprecated alias, use Config.merge()"""
+ self.merge(other)
+
+ def merge(self, other):
+ """merge another config object into this one"""
+ to_update = {}
for k, v in other.items():
- if k not in self:
- to_update[k] = v
- else: # I have this key
- if isinstance(v, Config) and isinstance(self[k], Config):
- # Recursively merge common sub Configs
- self[k].merge(v)
+ if k not in self:
+ to_update[k] = v
+ else: # I have this key
+ if isinstance(v, Config) and isinstance(self[k], Config):
+ # Recursively merge common sub Configs
+ self[k].merge(v)
elif isinstance(v, LazyConfigValue):
self[k] = v.merge_into(self[k])
- else:
- # Plain updates for non-Configs
- to_update[k] = v
-
- self.update(to_update)
-
- def collisions(self, other):
- """Check for collisions between two config objects.
-
- Returns a dict of the form {"Class": {"trait": "collision message"}}`,
- indicating which values have been ignored.
-
- An empty dict indicates no collisions.
- """
- collisions = {}
- for section in self:
- if section not in other:
- continue
- mine = self[section]
- theirs = other[section]
- for key in mine:
- if key in theirs and mine[key] != theirs[key]:
- collisions.setdefault(section, {})
- collisions[section][key] = "%r ignored, using %r" % (mine[key], theirs[key])
- return collisions
-
- def __contains__(self, key):
- # allow nested contains of the form `"Section.key" in config`
- if '.' in key:
- first, remainder = key.split('.', 1)
- if first not in self:
- return False
- return remainder in self[first]
-
- return super(Config, self).__contains__(key)
-
- # .has_key is deprecated for dictionaries.
- has_key = __contains__
-
- def _has_section(self, key):
- return _is_section_key(key) and key in self
-
- def copy(self):
- return type(self)(dict.copy(self))
-
- def __copy__(self):
- return self.copy()
-
- def __deepcopy__(self, memo):
- new_config = type(self)()
- for key, value in self.items():
- if isinstance(value, (Config, LazyConfigValue)):
- # deep copy config objects
- value = copy.deepcopy(value, memo)
- elif type(value) in {dict, list, set, tuple}:
- # shallow copy plain container traits
- value = copy.copy(value)
- new_config[key] = value
- return new_config
-
- def __getitem__(self, key):
- try:
- return dict.__getitem__(self, key)
- except KeyError:
- if _is_section_key(key):
- c = Config()
- dict.__setitem__(self, key, c)
- return c
- elif not key.startswith('_'):
- # undefined, create lazy value, used for container methods
- v = LazyConfigValue()
- dict.__setitem__(self, key, v)
- return v
- else:
- raise KeyError
-
- def __setitem__(self, key, value):
- if _is_section_key(key):
- if not isinstance(value, Config):
- raise ValueError('values whose keys begin with an uppercase '
- 'char must be Config instances: %r, %r' % (key, value))
- dict.__setitem__(self, key, value)
-
- def __getattr__(self, key):
- if key.startswith('__'):
- return dict.__getattr__(self, key)
- try:
- return self.__getitem__(key)
- except KeyError as e:
- raise AttributeError(e)
-
- def __setattr__(self, key, value):
- if key.startswith('__'):
- return dict.__setattr__(self, key, value)
- try:
- self.__setitem__(key, value)
- except KeyError as e:
- raise AttributeError(e)
-
- def __delattr__(self, key):
- if key.startswith('__'):
- return dict.__delattr__(self, key)
- try:
- dict.__delitem__(self, key)
- except KeyError as e:
- raise AttributeError(e)
-
-
+ else:
+ # Plain updates for non-Configs
+ to_update[k] = v
+
+ self.update(to_update)
+
+ def collisions(self, other):
+ """Check for collisions between two config objects.
+
+ Returns a dict of the form {"Class": {"trait": "collision message"}}`,
+ indicating which values have been ignored.
+
+ An empty dict indicates no collisions.
+ """
+ collisions = {}
+ for section in self:
+ if section not in other:
+ continue
+ mine = self[section]
+ theirs = other[section]
+ for key in mine:
+ if key in theirs and mine[key] != theirs[key]:
+ collisions.setdefault(section, {})
+ collisions[section][key] = "%r ignored, using %r" % (mine[key], theirs[key])
+ return collisions
+
+ def __contains__(self, key):
+ # allow nested contains of the form `"Section.key" in config`
+ if '.' in key:
+ first, remainder = key.split('.', 1)
+ if first not in self:
+ return False
+ return remainder in self[first]
+
+ return super(Config, self).__contains__(key)
+
+ # .has_key is deprecated for dictionaries.
+ has_key = __contains__
+
+ def _has_section(self, key):
+ return _is_section_key(key) and key in self
+
+ def copy(self):
+ return type(self)(dict.copy(self))
+
+ def __copy__(self):
+ return self.copy()
+
+ def __deepcopy__(self, memo):
+ new_config = type(self)()
+ for key, value in self.items():
+ if isinstance(value, (Config, LazyConfigValue)):
+ # deep copy config objects
+ value = copy.deepcopy(value, memo)
+ elif type(value) in {dict, list, set, tuple}:
+ # shallow copy plain container traits
+ value = copy.copy(value)
+ new_config[key] = value
+ return new_config
+
+ def __getitem__(self, key):
+ try:
+ return dict.__getitem__(self, key)
+ except KeyError:
+ if _is_section_key(key):
+ c = Config()
+ dict.__setitem__(self, key, c)
+ return c
+ elif not key.startswith('_'):
+ # undefined, create lazy value, used for container methods
+ v = LazyConfigValue()
+ dict.__setitem__(self, key, v)
+ return v
+ else:
+ raise KeyError
+
+ def __setitem__(self, key, value):
+ if _is_section_key(key):
+ if not isinstance(value, Config):
+ raise ValueError('values whose keys begin with an uppercase '
+ 'char must be Config instances: %r, %r' % (key, value))
+ dict.__setitem__(self, key, value)
+
+ def __getattr__(self, key):
+ if key.startswith('__'):
+ return dict.__getattr__(self, key)
+ try:
+ return self.__getitem__(key)
+ except KeyError as e:
+ raise AttributeError(e)
+
+ def __setattr__(self, key, value):
+ if key.startswith('__'):
+ return dict.__setattr__(self, key, value)
+ try:
+ self.__setitem__(key, value)
+ except KeyError as e:
+ raise AttributeError(e)
+
+ def __delattr__(self, key):
+ if key.startswith('__'):
+ return dict.__delattr__(self, key)
+ try:
+ dict.__delitem__(self, key)
+ except KeyError as e:
+ raise AttributeError(e)
+
+
class DeferredConfig:
"""Class for deferred-evaluation of config from CLI"""
pass
@@ -455,95 +455,95 @@ class DeferredConfigList(list, DeferredConfig):
return '%s(%s)' % (self.__class__.__name__, self._super_repr())
-#-----------------------------------------------------------------------------
-# Config loading classes
-#-----------------------------------------------------------------------------
-
-
-class ConfigLoader(object):
- """A object for loading configurations from just about anywhere.
-
- The resulting configuration is packaged as a :class:`Config`.
-
- Notes
- -----
- A :class:`ConfigLoader` does one thing: load a config from a source
- (file, command line arguments) and returns the data as a :class:`Config` object.
- There are lots of things that :class:`ConfigLoader` does not do. It does
- not implement complex logic for finding config files. It does not handle
- default values or merge multiple configs. These things need to be
- handled elsewhere.
- """
-
- def _log_default(self):
- from traitlets.log import get_logger
- return get_logger()
-
- def __init__(self, log=None):
- """A base class for config loaders.
-
- log : instance of :class:`logging.Logger` to use.
+#-----------------------------------------------------------------------------
+# Config loading classes
+#-----------------------------------------------------------------------------
+
+
+class ConfigLoader(object):
+ """A object for loading configurations from just about anywhere.
+
+ The resulting configuration is packaged as a :class:`Config`.
+
+ Notes
+ -----
+ A :class:`ConfigLoader` does one thing: load a config from a source
+ (file, command line arguments) and returns the data as a :class:`Config` object.
+ There are lots of things that :class:`ConfigLoader` does not do. It does
+ not implement complex logic for finding config files. It does not handle
+ default values or merge multiple configs. These things need to be
+ handled elsewhere.
+ """
+
+ def _log_default(self):
+ from traitlets.log import get_logger
+ return get_logger()
+
+ def __init__(self, log=None):
+ """A base class for config loaders.
+
+ log : instance of :class:`logging.Logger` to use.
By default logger of :meth:`traitlets.config.application.Application.instance()`
- will be used
-
- Examples
- --------
- >>> cl = ConfigLoader()
- >>> config = cl.load_config()
- >>> config
- {}
- """
- self.clear()
- if log is None:
- self.log = self._log_default()
- self.log.debug('Using default logger')
- else:
- self.log = log
-
- def clear(self):
- self.config = Config()
-
- def load_config(self):
- """Load a config from somewhere, return a :class:`Config` instance.
-
- Usually, this will cause self.config to be set and then returned.
- However, in most cases, :meth:`ConfigLoader.clear` should be called
- to erase any previous state.
- """
- self.clear()
- return self.config
-
-
-class FileConfigLoader(ConfigLoader):
- """A base class for file based configurations.
-
- As we add more file based config loaders, the common logic should go
- here.
- """
-
- def __init__(self, filename, path=None, **kw):
- """Build a config loader for a filename and path.
-
- Parameters
- ----------
- filename : str
- The file name of the config file.
- path : str, list, tuple
- The path to search for the config file on, or a sequence of
- paths to try in order.
- """
- super(FileConfigLoader, self).__init__(**kw)
- self.filename = filename
- self.path = path
- self.full_filename = ''
-
- def _find_file(self):
- """Try to find the file by searching the paths."""
- self.full_filename = filefind(self.filename, self.path)
-
-class JSONFileConfigLoader(FileConfigLoader):
+ will be used
+
+ Examples
+ --------
+ >>> cl = ConfigLoader()
+ >>> config = cl.load_config()
+ >>> config
+ {}
+ """
+ self.clear()
+ if log is None:
+ self.log = self._log_default()
+ self.log.debug('Using default logger')
+ else:
+ self.log = log
+
+ def clear(self):
+ self.config = Config()
+
+ def load_config(self):
+ """Load a config from somewhere, return a :class:`Config` instance.
+
+ Usually, this will cause self.config to be set and then returned.
+ However, in most cases, :meth:`ConfigLoader.clear` should be called
+ to erase any previous state.
+ """
+ self.clear()
+ return self.config
+
+
+class FileConfigLoader(ConfigLoader):
+ """A base class for file based configurations.
+
+ As we add more file based config loaders, the common logic should go
+ here.
+ """
+
+ def __init__(self, filename, path=None, **kw):
+ """Build a config loader for a filename and path.
+
+ Parameters
+ ----------
+ filename : str
+ The file name of the config file.
+ path : str, list, tuple
+ The path to search for the config file on, or a sequence of
+ paths to try in order.
+ """
+ super(FileConfigLoader, self).__init__(**kw)
+ self.filename = filename
+ self.path = path
+ self.full_filename = ''
+
+ def _find_file(self):
+ """Try to find the file by searching the paths."""
+ self.full_filename = filefind(self.filename, self.path)
+
+class JSONFileConfigLoader(FileConfigLoader):
"""A JSON file loader for config
-
+
Can also act as a context manager that rewrite the configuration file to disk on exit.
Example::
@@ -553,36 +553,36 @@ class JSONFileConfigLoader(FileConfigLoader):
"""
- def load_config(self):
- """Load the config from a file and return it as a Config object."""
- self.clear()
- try:
- self._find_file()
- except IOError as e:
- raise ConfigFileNotFound(str(e))
- dct = self._read_file_as_dict()
- self.config = self._convert_to_config(dct)
- return self.config
-
- def _read_file_as_dict(self):
- with open(self.full_filename) as f:
- return json.load(f)
-
- def _convert_to_config(self, dictionary):
- if 'version' in dictionary:
- version = dictionary.pop('version')
- else:
- version = 1
-
- if version == 1:
- return Config(dictionary)
- else:
- raise ValueError('Unknown version of JSON config file: {version}'.format(version=version))
-
+ def load_config(self):
+ """Load the config from a file and return it as a Config object."""
+ self.clear()
+ try:
+ self._find_file()
+ except IOError as e:
+ raise ConfigFileNotFound(str(e))
+ dct = self._read_file_as_dict()
+ self.config = self._convert_to_config(dct)
+ return self.config
+
+ def _read_file_as_dict(self):
+ with open(self.full_filename) as f:
+ return json.load(f)
+
+ def _convert_to_config(self, dictionary):
+ if 'version' in dictionary:
+ version = dictionary.pop('version')
+ else:
+ version = 1
+
+ if version == 1:
+ return Config(dictionary)
+ else:
+ raise ValueError('Unknown version of JSON config file: {version}'.format(version=version))
+
def __enter__(self):
self.load_config()
return self.config
-
+
def __exit__(self, exc_type, exc_value, traceback):
"""
Exit the context manager but do not handle any errors.
@@ -597,68 +597,68 @@ class JSONFileConfigLoader(FileConfigLoader):
-class PyFileConfigLoader(FileConfigLoader):
- """A config loader for pure python files.
-
- This is responsible for locating a Python config file by filename and
- path, then executing it to construct a Config object.
- """
-
- def load_config(self):
- """Load the config from a file and return it as a Config object."""
- self.clear()
- try:
- self._find_file()
- except IOError as e:
- raise ConfigFileNotFound(str(e))
- self._read_file_as_dict()
- return self.config
-
- def load_subconfig(self, fname, path=None):
- """Injected into config file namespace as load_subconfig"""
- if path is None:
- path = self.path
-
- loader = self.__class__(fname, path)
- try:
- sub_config = loader.load_config()
- except ConfigFileNotFound:
- # Pass silently if the sub config is not there,
- # treat it as an empty config file.
- pass
- else:
- self.config.merge(sub_config)
-
- def _read_file_as_dict(self):
- """Load the config file into self.config, with recursive loading."""
- def get_config():
- """Unnecessary now, but a deprecation warning is more trouble than it's worth."""
- return self.config
-
- namespace = dict(
- c=self.config,
- load_subconfig=self.load_subconfig,
- get_config=get_config,
- __file__=self.full_filename,
- )
+class PyFileConfigLoader(FileConfigLoader):
+ """A config loader for pure python files.
+
+ This is responsible for locating a Python config file by filename and
+ path, then executing it to construct a Config object.
+ """
+
+ def load_config(self):
+ """Load the config from a file and return it as a Config object."""
+ self.clear()
+ try:
+ self._find_file()
+ except IOError as e:
+ raise ConfigFileNotFound(str(e))
+ self._read_file_as_dict()
+ return self.config
+
+ def load_subconfig(self, fname, path=None):
+ """Injected into config file namespace as load_subconfig"""
+ if path is None:
+ path = self.path
+
+ loader = self.__class__(fname, path)
+ try:
+ sub_config = loader.load_config()
+ except ConfigFileNotFound:
+ # Pass silently if the sub config is not there,
+ # treat it as an empty config file.
+ pass
+ else:
+ self.config.merge(sub_config)
+
+ def _read_file_as_dict(self):
+ """Load the config file into self.config, with recursive loading."""
+ def get_config():
+ """Unnecessary now, but a deprecation warning is more trouble than it's worth."""
+ return self.config
+
+ namespace = dict(
+ c=self.config,
+ load_subconfig=self.load_subconfig,
+ get_config=get_config,
+ __file__=self.full_filename,
+ )
conf_filename = self.full_filename
with open(conf_filename, 'rb') as f:
exec(compile(f.read(), conf_filename, 'exec'), namespace, namespace)
-
-
-class CommandLineConfigLoader(ConfigLoader):
- """A config loader for command line arguments.
-
- As we add more command line based loaders, the common logic should go
- here.
- """
-
+
+
+class CommandLineConfigLoader(ConfigLoader):
+ """A config loader for command line arguments.
+
+ As we add more command line based loaders, the common logic should go
+ here.
+ """
+
def _exec_config_str(self, lhs, rhs, trait=None):
- """execute self.config.<lhs> = <rhs>
+ """execute self.config.<lhs> = <rhs>
- * expands ~ with expanduser
+ * expands ~ with expanduser
* interprets value with trait if available
- """
+ """
value = rhs
if isinstance(value, DeferredConfig):
if trait:
@@ -672,41 +672,41 @@ class CommandLineConfigLoader(ConfigLoader):
value = trait.from_string(value)
else:
value = DeferredConfigString(value)
-
+
*path, key = lhs.split(".")
section = self.config
for part in path:
section = section[part]
section[key] = value
return
-
- def _load_flag(self, cfg):
- """update self.config from a flag, which can be a dict or Config"""
- if isinstance(cfg, (dict, Config)):
- # don't clobber whole config sections, update
- # each section from config:
+
+ def _load_flag(self, cfg):
+ """update self.config from a flag, which can be a dict or Config"""
+ if isinstance(cfg, (dict, Config)):
+ # don't clobber whole config sections, update
+ # each section from config:
for sec, c in cfg.items():
- self.config[sec].update(c)
- else:
- raise TypeError("Invalid flag: %r" % cfg)
-
+ self.config[sec].update(c)
+ else:
+ raise TypeError("Invalid flag: %r" % cfg)
+
# match --Class.trait keys for argparse
# matches:
# --Class.trait
# --x
# -x
-
+
class_trait_opt_pattern = re.compile(r'^\-?\-[A-Za-z][\w]*(\.[\w]+)*$')
-
+
_DOT_REPLACEMENT = "__DOT__"
_DASH_REPLACEMENT = "__DASH__"
-
-
+
+
class _KVAction(argparse.Action):
"""Custom argparse action for handling --Class.trait=x
-
+
Always
- """
+ """
def __call__(self, parser, namespace, values, option_string=None):
if isinstance(values, str):
values = [values]
@@ -718,11 +718,11 @@ class _KVAction(argparse.Action):
items = DeferredConfigList(items)
items.extend(values)
setattr(namespace, self.dest, items)
-
-
+
+
class _DefaultOptionDict(dict):
"""Like the default options dict
-
+
but acts as if all --Class.trait options are predefined
"""
def _add_kv_action(self, key):
@@ -732,31 +732,31 @@ class _DefaultOptionDict(dict):
# use metavar for display purposes
metavar=key.lstrip("-"),
)
-
+
def __contains__(self, key):
if '=' in key:
return False
if super().__contains__(key):
return True
-
+
if key.startswith("-") and class_trait_opt_pattern.match(key):
self._add_kv_action(key)
return True
return False
-
+
def __getitem__(self, key):
if key in self:
return super().__getitem__(key)
else:
raise KeyError(key)
-
+
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
-
-
+
+
class _KVArgParser(argparse.ArgumentParser):
"""subclass of ArgumentParser where any --Class.trait option is implicitly defined"""
def parse_known_args(self, args=None, namespace=None):
@@ -766,23 +766,23 @@ class _KVArgParser(argparse.ArgumentParser):
container._option_string_actions = _DefaultOptionDict(
container._option_string_actions)
return super().parse_known_args(args, namespace)
-
-
-class ArgParseConfigLoader(CommandLineConfigLoader):
- """A loader that uses the argparse module to load from the command line."""
-
+
+
+class ArgParseConfigLoader(CommandLineConfigLoader):
+ """A loader that uses the argparse module to load from the command line."""
+
parser_class = ArgumentParser
def __init__(self, argv=None, aliases=None, flags=None, log=None, classes=(),
*parser_args, **parser_kw):
- """Create a config loader for use with argparse.
-
- Parameters
- ----------
+ """Create a config loader for use with argparse.
+
+ Parameters
+ ----------
classes : optional, list
The classes to scan for *container* config-traits and decide
for their "multiplicity" when adding them as *argparse* arguments.
- argv : optional, list
+ argv : optional, list
If given, used to read command-line arguments from, otherwise
sys.argv[1:] is used.
*parser_args : tuple
@@ -797,39 +797,39 @@ class ArgParseConfigLoader(CommandLineConfigLoader):
Dict of flags to full traitlests names for CLI parsing
log
Passed to `ConfigLoader`
-
- Returns
- -------
- config : Config
- The resulting Config object.
- """
- super(CommandLineConfigLoader, self).__init__(log=log)
- self.clear()
- if argv is None:
- argv = sys.argv[1:]
- self.argv = argv
- self.aliases = aliases or {}
- self.flags = flags or {}
+
+ Returns
+ -------
+ config : Config
+ The resulting Config object.
+ """
+ super(CommandLineConfigLoader, self).__init__(log=log)
+ self.clear()
+ if argv is None:
+ argv = sys.argv[1:]
+ self.argv = argv
+ self.aliases = aliases or {}
+ self.flags = flags or {}
self.classes = classes
-
- self.parser_args = parser_args
- self.version = parser_kw.pop("version", None)
- kwargs = dict(argument_default=argparse.SUPPRESS)
- kwargs.update(parser_kw)
- self.parser_kw = kwargs
-
+
+ self.parser_args = parser_args
+ self.version = parser_kw.pop("version", None)
+ kwargs = dict(argument_default=argparse.SUPPRESS)
+ kwargs.update(parser_kw)
+ self.parser_kw = kwargs
+
def load_config(self, argv=None, aliases=None, flags=_deprecated, classes=None):
- """Parse command line arguments and return as a Config object.
-
- Parameters
- ----------
+ """Parse command line arguments and return as a Config object.
+
+ Parameters
+ ----------
argv : optional, list
If given, a list with the structure of sys.argv[1:] to parse
arguments from. If not given, the instance's self.argv attribute
(given at construction time) is used.
flags
Deprecated in traitlets 5.0, instanciate the config loader with the flags.
-
+
"""
if flags is not _deprecated:
@@ -840,35 +840,35 @@ class ArgParseConfigLoader(CommandLineConfigLoader):
stacklevel=2,
)
- self.clear()
- if argv is None:
- argv = self.argv
+ self.clear()
+ if argv is None:
+ argv = self.argv
if aliases is not None:
self.aliases = aliases
if classes is not None:
self.classes = classes
self._create_parser()
- self._parse_args(argv)
- self._convert_to_config()
- return self.config
-
- def get_extra_args(self):
- if hasattr(self, 'extra_args'):
- return self.extra_args
- else:
- return []
-
+ self._parse_args(argv)
+ self._convert_to_config()
+ return self.config
+
+ def get_extra_args(self):
+ if hasattr(self, 'extra_args'):
+ return self.extra_args
+ else:
+ return []
+
def _create_parser(self):
self.parser = self.parser_class(*self.parser_args, **self.parser_kw)
self._add_arguments(self.aliases, self.flags, self.classes)
-
+
def _add_arguments(self, aliases, flags, classes):
- raise NotImplementedError("subclasses must implement _add_arguments")
-
- def _parse_args(self, args):
- """self.parser->self.parsed_data"""
+ raise NotImplementedError("subclasses must implement _add_arguments")
+
+ def _parse_args(self, args):
+ """self.parser->self.parsed_data"""
uargs = [cast_unicode(a) for a in args]
-
+
unpacked_aliases = {}
if self.aliases:
unpacked_aliases = {}
@@ -908,15 +908,15 @@ class ArgParseConfigLoader(CommandLineConfigLoader):
self.parsed_data = self.parser.parse_args(to_parse)
self.extra_args = extra_args
- def _convert_to_config(self):
- """self.parsed_data->self.config"""
+ def _convert_to_config(self):
+ """self.parsed_data->self.config"""
for k, v in vars(self.parsed_data).items():
*path, key = k.split(".")
section = self.config
for p in path:
section = section[p]
setattr(section, key, v)
-
+
class _FlagAction(argparse.Action):
"""ArgParse action to handle a flag"""
@@ -937,9 +937,9 @@ class _FlagAction(argparse.Action):
setattr(namespace, self.alias, values)
-class KVArgParseConfigLoader(ArgParseConfigLoader):
- """A config loader that loads aliases and flags with argparse,
-
+class KVArgParseConfigLoader(ArgParseConfigLoader):
+ """A config loader that loads aliases and flags with argparse,
+
as well as arbitrary --Class.trait value
"""
@@ -947,10 +947,10 @@ class KVArgParseConfigLoader(ArgParseConfigLoader):
def _add_arguments(self, aliases, flags, classes):
alias_flags = {}
- paa = self.parser.add_argument
+ paa = self.parser.add_argument
self.parser.set_defaults(_flags=[])
paa("extra_args", nargs="*")
-
+
## An index of all container traits collected::
#
# { <traitname>: (<trait>, <argparse-kwds>) }
@@ -1010,10 +1010,10 @@ class KVArgParseConfigLoader(ArgParseConfigLoader):
keys = ('-' + key, '--' + key) if len(key) == 1 else ('--'+ key,)
paa(*keys, **argparse_kwds)
- def _convert_to_config(self):
- """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
+ def _convert_to_config(self):
+ """self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
extra_args = self.extra_args
-
+
for lhs, rhs in vars(self.parsed_data).items():
if lhs == "extra_args":
self.extra_args = ["-" if a == _DASH_REPLACEMENT else a for a in rhs] + extra_args
@@ -1021,7 +1021,7 @@ class KVArgParseConfigLoader(ArgParseConfigLoader):
elif lhs == '_flags':
# _flags will be handled later
continue
-
+
lhs = lhs.replace(_DOT_REPLACEMENT, ".")
if '.' not in lhs:
# probably a mistyped alias, but not technically illegal
@@ -1048,12 +1048,12 @@ class KVArgParseConfigLoader(ArgParseConfigLoader):
raise ArgumentError(f"Error loading argument {lhs}={rhs}, {e}")
for subc in self.parsed_data._flags:
- self._load_flag(subc)
-
-
+ self._load_flag(subc)
+
+
class KeyValueConfigLoader(KVArgParseConfigLoader):
"""Deprecated in traitlets 5.0
-
+
Use KVArgParseConfigLoader
"""
def __init__(self, *args, **kwargs):
@@ -1066,25 +1066,25 @@ class KeyValueConfigLoader(KVArgParseConfigLoader):
super().__init__(*args, **kwargs)
-def load_pyconfig_files(config_files, path):
- """Load multiple Python config files, merging each of them in turn.
-
- Parameters
+def load_pyconfig_files(config_files, path):
+ """Load multiple Python config files, merging each of them in turn.
+
+ Parameters
----------
- config_files : list of str
- List of config files names to load and merge into the config.
- path : unicode
- The full path to the location of the config files.
- """
- config = Config()
- for cf in config_files:
- loader = PyFileConfigLoader(cf, path=path)
- try:
- next_config = loader.load_config()
- except ConfigFileNotFound:
- pass
- except:
- raise
- else:
- config.merge(next_config)
- return config
+ config_files : list of str
+ List of config files names to load and merge into the config.
+ path : unicode
+ The full path to the location of the config files.
+ """
+ config = Config()
+ for cf in config_files:
+ loader = PyFileConfigLoader(cf, path=path)
+ try:
+ next_config = loader.load_config()
+ except ConfigFileNotFound:
+ pass
+ except:
+ raise
+ else:
+ config.merge(next_config)
+ return config
diff --git a/contrib/python/traitlets/py3/traitlets/config/manager.py b/contrib/python/traitlets/py3/traitlets/config/manager.py
index 164053261e..041477b1b9 100644
--- a/contrib/python/traitlets/py3/traitlets/config/manager.py
+++ b/contrib/python/traitlets/py3/traitlets/config/manager.py
@@ -1,84 +1,84 @@
-"""Manager to read and modify config data in JSON files.
-"""
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-import errno
-import io
-import json
-import os
-
-from traitlets.config import LoggingConfigurable
-from traitlets.traitlets import Unicode
-
-
-def recursive_update(target, new):
- """Recursively update one dictionary using another.
-
- None values will delete their keys.
- """
- for k, v in new.items():
- if isinstance(v, dict):
- if k not in target:
- target[k] = {}
- recursive_update(target[k], v)
- if not target[k]:
- # Prune empty subdicts
- del target[k]
-
- elif v is None:
- target.pop(k, None)
-
- else:
- target[k] = v
-
-
-class BaseJSONConfigManager(LoggingConfigurable):
- """General JSON config manager
-
- Deals with persisting/storing config in a json file
- """
-
- config_dir = Unicode('.')
-
- def ensure_config_dir_exists(self):
- try:
- os.makedirs(self.config_dir, 0o755)
- except OSError as e:
- if e.errno != errno.EEXIST:
- raise
-
- def file_name(self, section_name):
- return os.path.join(self.config_dir, section_name+'.json')
-
- def get(self, section_name):
- """Retrieve the config data for the specified section.
-
- Returns the data as a dictionary, or an empty dictionary if the file
- doesn't exist.
- """
- filename = self.file_name(section_name)
- if os.path.isfile(filename):
- with io.open(filename, encoding='utf-8') as f:
- return json.load(f)
- else:
- return {}
-
- def set(self, section_name, data):
- """Store the given config data.
- """
- filename = self.file_name(section_name)
- self.ensure_config_dir_exists()
-
+"""Manager to read and modify config data in JSON files.
+"""
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+import errno
+import io
+import json
+import os
+
+from traitlets.config import LoggingConfigurable
+from traitlets.traitlets import Unicode
+
+
+def recursive_update(target, new):
+ """Recursively update one dictionary using another.
+
+ None values will delete their keys.
+ """
+ for k, v in new.items():
+ if isinstance(v, dict):
+ if k not in target:
+ target[k] = {}
+ recursive_update(target[k], v)
+ if not target[k]:
+ # Prune empty subdicts
+ del target[k]
+
+ elif v is None:
+ target.pop(k, None)
+
+ else:
+ target[k] = v
+
+
+class BaseJSONConfigManager(LoggingConfigurable):
+ """General JSON config manager
+
+ Deals with persisting/storing config in a json file
+ """
+
+ config_dir = Unicode('.')
+
+ def ensure_config_dir_exists(self):
+ try:
+ os.makedirs(self.config_dir, 0o755)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ def file_name(self, section_name):
+ return os.path.join(self.config_dir, section_name+'.json')
+
+ def get(self, section_name):
+ """Retrieve the config data for the specified section.
+
+ Returns the data as a dictionary, or an empty dictionary if the file
+ doesn't exist.
+ """
+ filename = self.file_name(section_name)
+ if os.path.isfile(filename):
+ with io.open(filename, encoding='utf-8') as f:
+ return json.load(f)
+ else:
+ return {}
+
+ def set(self, section_name, data):
+ """Store the given config data.
+ """
+ filename = self.file_name(section_name)
+ self.ensure_config_dir_exists()
+
f = open(filename, 'w', encoding='utf-8')
- with f:
- json.dump(data, f, indent=2)
-
- def update(self, section_name, new_data):
- """Modify the config section by recursively updating it with new_data.
-
- Returns the modified config data as a dictionary.
- """
- data = self.get(section_name)
- recursive_update(data, new_data)
- self.set(section_name, data)
- return data
+ with f:
+ json.dump(data, f, indent=2)
+
+ def update(self, section_name, new_data):
+ """Modify the config section by recursively updating it with new_data.
+
+ Returns the modified config data as a dictionary.
+ """
+ data = self.get(section_name)
+ recursive_update(data, new_data)
+ self.set(section_name, data)
+ return data
diff --git a/contrib/python/traitlets/py3/traitlets/log.py b/contrib/python/traitlets/py3/traitlets/log.py
index af86b325f5..559735bd1a 100644
--- a/contrib/python/traitlets/py3/traitlets/log.py
+++ b/contrib/python/traitlets/py3/traitlets/log.py
@@ -1,27 +1,27 @@
-"""Grab the global logger instance."""
+"""Grab the global logger instance."""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import logging
+
+_logger = None
+
+def get_logger():
+ """Grab the global logger instance.
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
+ If a global Application is instantiated, grab its logger.
+ Otherwise, grab the root logger.
+ """
+ global _logger
-import logging
-
-_logger = None
-
-def get_logger():
- """Grab the global logger instance.
-
- If a global Application is instantiated, grab its logger.
- Otherwise, grab the root logger.
- """
- global _logger
-
- if _logger is None:
- from .config import Application
- if Application.initialized():
- _logger = Application.instance().log
- else:
+ if _logger is None:
+ from .config import Application
+ if Application.initialized():
+ _logger = Application.instance().log
+ else:
_logger = logging.getLogger('traitlets')
# Add a NullHandler to silence warnings about not being
# initialized, per best practice for libraries.
_logger.addHandler(logging.NullHandler())
- return _logger
+ return _logger
diff --git a/contrib/python/traitlets/py3/traitlets/traitlets.py b/contrib/python/traitlets/py3/traitlets/traitlets.py
index 6bdf7414d3..1a278992a3 100644
--- a/contrib/python/traitlets/py3/traitlets/traitlets.py
+++ b/contrib/python/traitlets/py3/traitlets/traitlets.py
@@ -1,62 +1,62 @@
-"""
-A lightweight Traits like module.
-
-This is designed to provide a lightweight, simple, pure Python version of
-many of the capabilities of enthought.traits. This includes:
-
-* Validation
-* Type specification with defaults
-* Static and dynamic notification
-* Basic predefined types
-* An API that is similar to enthought.traits
-
-We don't support:
-
-* Delegation
-* Automatic GUI generation
-* A full set of trait types. Most importantly, we don't provide container
- traits (list, dict, tuple) that can trigger notifications if their
- contents change.
-* API compatibility with enthought.traits
-
-There are also some important difference in our design:
-
-* enthought.traits does not validate default values. We do.
-
-We choose to create this module because we need these capabilities, but
-we need them to be pure Python so they work in all Python implementations,
-including Jython and IronPython.
-
-Inheritance diagram:
-
-.. inheritance-diagram:: traitlets.traitlets
- :parts: 3
-"""
-
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-#
-# Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
-# also under the terms of the Modified BSD License.
-
+"""
+A lightweight Traits like module.
+
+This is designed to provide a lightweight, simple, pure Python version of
+many of the capabilities of enthought.traits. This includes:
+
+* Validation
+* Type specification with defaults
+* Static and dynamic notification
+* Basic predefined types
+* An API that is similar to enthought.traits
+
+We don't support:
+
+* Delegation
+* Automatic GUI generation
+* A full set of trait types. Most importantly, we don't provide container
+ traits (list, dict, tuple) that can trigger notifications if their
+ contents change.
+* API compatibility with enthought.traits
+
+There are also some important difference in our design:
+
+* enthought.traits does not validate default values. We do.
+
+We choose to create this module because we need these capabilities, but
+we need them to be pure Python so they work in all Python implementations,
+including Jython and IronPython.
+
+Inheritance diagram:
+
+.. inheritance-diagram:: traitlets.traitlets
+ :parts: 3
+"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+#
+# Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
+# also under the terms of the Modified BSD License.
+
from ast import literal_eval
-import contextlib
-import inspect
+import contextlib
+import inspect
import os
-import re
-import sys
-import types
+import re
+import sys
+import types
import enum
-from warnings import warn, warn_explicit
-
-from .utils.getargspec import getargspec
-from .utils.importstring import import_item
-from .utils.sentinel import Sentinel
+from warnings import warn, warn_explicit
+
+from .utils.getargspec import getargspec
+from .utils.importstring import import_item
+from .utils.sentinel import Sentinel
from .utils.bunch import Bunch
from .utils.descriptions import describe, class_of, add_article, repr_type
-
-SequenceTypes = (list, tuple, set, frozenset)
-
+
+SequenceTypes = (list, tuple, set, frozenset)
+
# backward compatibility, use to differ between Python 2 and 3.
ClassTypes = (type,)
@@ -85,34 +85,34 @@ __all__ = [
# any TraitType subclass (that doesn't start with _) will be added automatically
-#-----------------------------------------------------------------------------
-# Basic classes
-#-----------------------------------------------------------------------------
-
-
-Undefined = Sentinel('Undefined', 'traitlets',
-'''
-Used in Traitlets to specify that no defaults are set in kwargs
-'''
-)
-
-All = Sentinel('All', 'traitlets',
-'''
-Used in Traitlets to listen to all types of notification or to notifications
-from all trait attributes.
-'''
-)
-
-# Deprecated alias
-NoDefaultSpecified = Undefined
-
-class TraitError(Exception):
- pass
-
-#-----------------------------------------------------------------------------
-# Utilities
-#-----------------------------------------------------------------------------
-
+#-----------------------------------------------------------------------------
+# Basic classes
+#-----------------------------------------------------------------------------
+
+
+Undefined = Sentinel('Undefined', 'traitlets',
+'''
+Used in Traitlets to specify that no defaults are set in kwargs
+'''
+)
+
+All = Sentinel('All', 'traitlets',
+'''
+Used in Traitlets to listen to all types of notification or to notifications
+from all trait attributes.
+'''
+)
+
+# Deprecated alias
+NoDefaultSpecified = Undefined
+
+class TraitError(Exception):
+ pass
+
+#-----------------------------------------------------------------------------
+# Utilities
+#-----------------------------------------------------------------------------
+
_name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
def isidentifier(s):
@@ -134,332 +134,332 @@ def _should_warn(key):
else:
return False
-def _deprecated_method(method, cls, method_name, msg):
- """Show deprecation warning about a magic method definition.
-
- Uses warn_explicit to bind warning to method definition instead of triggering code,
- which isn't relevant.
- """
+def _deprecated_method(method, cls, method_name, msg):
+ """Show deprecation warning about a magic method definition.
+
+ Uses warn_explicit to bind warning to method definition instead of triggering code,
+ which isn't relevant.
+ """
warn_msg = "{classname}.{method_name} is deprecated in traitlets 4.1: {msg}".format(
- classname=cls.__name__, method_name=method_name, msg=msg
- )
-
- for parent in inspect.getmro(cls):
- if method_name in parent.__dict__:
- cls = parent
- break
+ classname=cls.__name__, method_name=method_name, msg=msg
+ )
+
+ for parent in inspect.getmro(cls):
+ if method_name in parent.__dict__:
+ cls = parent
+ break
# limit deprecation messages to once per package
package_name = cls.__module__.split('.', 1)[0]
key = (package_name, msg)
if not _should_warn(key):
return
- try:
- fname = inspect.getsourcefile(method) or "<unknown>"
- lineno = inspect.getsourcelines(method)[1] or 0
+ try:
+ fname = inspect.getsourcefile(method) or "<unknown>"
+ lineno = inspect.getsourcelines(method)[1] or 0
except (OSError, TypeError) as e:
- # Failed to inspect for some reason
- warn(warn_msg + ('\n(inspection failed) %s' % e), DeprecationWarning)
- else:
- warn_explicit(warn_msg, DeprecationWarning, fname, lineno)
-
+ # Failed to inspect for some reason
+ warn(warn_msg + ('\n(inspection failed) %s' % e), DeprecationWarning)
+ else:
+ warn_explicit(warn_msg, DeprecationWarning, fname, lineno)
+
def _safe_literal_eval(s):
"""Safely evaluate an expression
-
+
Returns original string if eval fails.
-
+
Use only where types are ambiguous.
- """
+ """
try:
return literal_eval(s)
except (NameError, SyntaxError, ValueError):
return s
-
-def is_trait(t):
- """ Returns whether the given value is an instance or subclass of TraitType.
- """
- return (isinstance(t, TraitType) or
- (isinstance(t, type) and issubclass(t, TraitType)))
-
-
-def parse_notifier_name(names):
- """Convert the name argument to a list of names.
-
- Examples
- --------
- >>> parse_notifier_name([])
- [All]
+
+def is_trait(t):
+ """ Returns whether the given value is an instance or subclass of TraitType.
+ """
+ return (isinstance(t, TraitType) or
+ (isinstance(t, type) and issubclass(t, TraitType)))
+
+
+def parse_notifier_name(names):
+ """Convert the name argument to a list of names.
+
+ Examples
+ --------
+ >>> parse_notifier_name([])
+ [All]
>>> parse_notifier_name("a")
- ['a']
+ ['a']
>>> parse_notifier_name(["a", "b"])
- ['a', 'b']
- >>> parse_notifier_name(All)
- [All]
- """
+ ['a', 'b']
+ >>> parse_notifier_name(All)
+ [All]
+ """
if names is All or isinstance(names, str):
- return [names]
+ return [names]
else:
- if not names or All in names:
- return [All]
- for n in names:
+ if not names or All in names:
+ return [All]
+ for n in names:
if not isinstance(n, str):
raise TypeError("names must be strings, not %r" % n)
- return names
-
-
-class _SimpleTest:
- def __init__ ( self, value ): self.value = value
- def __call__ ( self, test ):
- return test == self.value
- def __repr__(self):
- return "<SimpleTest(%r)" % self.value
- def __str__(self):
- return self.__repr__()
-
-
-def getmembers(object, predicate=None):
- """A safe version of inspect.getmembers that handles missing attributes.
-
- This is useful when there are descriptor based attributes that for
- some reason raise AttributeError even though they exist. This happens
- in zope.inteface with the __provides__ attribute.
- """
- results = []
- for key in dir(object):
- try:
- value = getattr(object, key)
- except AttributeError:
- pass
- else:
- if not predicate or predicate(value):
- results.append((key, value))
- results.sort()
- return results
-
-def _validate_link(*tuples):
- """Validate arguments for traitlet link functions"""
- for t in tuples:
- if not len(t) == 2:
- raise TypeError("Each linked traitlet must be specified as (HasTraits, 'trait_name'), not %r" % t)
- obj, trait_name = t
- if not isinstance(obj, HasTraits):
- raise TypeError("Each object must be HasTraits, not %r" % type(obj))
- if not trait_name in obj.traits():
- raise TypeError("%r has no trait %r" % (obj, trait_name))
-
-class link(object):
- """Link traits from different objects together so they remain in sync.
-
- Parameters
- ----------
- source : (object / attribute name) pair
- target : (object / attribute name) pair
+ return names
+
+
+class _SimpleTest:
+ def __init__ ( self, value ): self.value = value
+ def __call__ ( self, test ):
+ return test == self.value
+ def __repr__(self):
+ return "<SimpleTest(%r)" % self.value
+ def __str__(self):
+ return self.__repr__()
+
+
+def getmembers(object, predicate=None):
+ """A safe version of inspect.getmembers that handles missing attributes.
+
+ This is useful when there are descriptor based attributes that for
+ some reason raise AttributeError even though they exist. This happens
+ in zope.inteface with the __provides__ attribute.
+ """
+ results = []
+ for key in dir(object):
+ try:
+ value = getattr(object, key)
+ except AttributeError:
+ pass
+ else:
+ if not predicate or predicate(value):
+ results.append((key, value))
+ results.sort()
+ return results
+
+def _validate_link(*tuples):
+ """Validate arguments for traitlet link functions"""
+ for t in tuples:
+ if not len(t) == 2:
+ raise TypeError("Each linked traitlet must be specified as (HasTraits, 'trait_name'), not %r" % t)
+ obj, trait_name = t
+ if not isinstance(obj, HasTraits):
+ raise TypeError("Each object must be HasTraits, not %r" % type(obj))
+ if not trait_name in obj.traits():
+ raise TypeError("%r has no trait %r" % (obj, trait_name))
+
+class link(object):
+ """Link traits from different objects together so they remain in sync.
+
+ Parameters
+ ----------
+ source : (object / attribute name) pair
+ target : (object / attribute name) pair
transform: iterable with two callables (optional)
Data transformation between source and target and target and source.
-
- Examples
- --------
+
+ Examples
+ --------
>>> c = link((src, "value"), (tgt, "value"))
- >>> src.value = 5 # updates other objects as well
- """
- updating = False
-
+ >>> src.value = 5 # updates other objects as well
+ """
+ updating = False
+
def __init__(self, source, target, transform=None):
- _validate_link(source, target)
- self.source, self.target = source, target
+ _validate_link(source, target)
+ self.source, self.target = source, target
self._transform, self._transform_inv = (
transform if transform else (lambda x: x,) * 2)
self.link()
def link(self):
- try:
+ try:
setattr(self.target[0], self.target[1],
self._transform(getattr(self.source[0], self.source[1])))
- finally:
+ finally:
self.source[0].observe(self._update_target, names=self.source[1])
self.target[0].observe(self._update_source, names=self.target[1])
-
- @contextlib.contextmanager
- def _busy_updating(self):
- self.updating = True
- try:
- yield
- finally:
- self.updating = False
-
- def _update_target(self, change):
- if self.updating:
- return
- with self._busy_updating():
+
+ @contextlib.contextmanager
+ def _busy_updating(self):
+ self.updating = True
+ try:
+ yield
+ finally:
+ self.updating = False
+
+ def _update_target(self, change):
+ if self.updating:
+ return
+ with self._busy_updating():
setattr(self.target[0], self.target[1], self._transform(change.new))
if getattr(self.source[0], self.source[1]) != change.new:
raise TraitError(
"Broken link {}: the source value changed while updating "
"the target.".format(self))
-
- def _update_source(self, change):
- if self.updating:
- return
- with self._busy_updating():
+
+ def _update_source(self, change):
+ if self.updating:
+ return
+ with self._busy_updating():
setattr(self.source[0], self.source[1],
self._transform_inv(change.new))
if getattr(self.target[0], self.target[1]) != change.new:
raise TraitError(
"Broken link {}: the target value changed while updating "
"the source.".format(self))
-
- def unlink(self):
- self.source[0].unobserve(self._update_target, names=self.source[1])
- self.target[0].unobserve(self._update_source, names=self.target[1])
-
-
-class directional_link(object):
- """Link the trait of a source object with traits of target objects.
-
- Parameters
- ----------
- source : (object, attribute name) pair
- target : (object, attribute name) pair
- transform: callable (optional)
- Data transformation between source and target.
-
- Examples
- --------
+
+ def unlink(self):
+ self.source[0].unobserve(self._update_target, names=self.source[1])
+ self.target[0].unobserve(self._update_source, names=self.target[1])
+
+
+class directional_link(object):
+ """Link the trait of a source object with traits of target objects.
+
+ Parameters
+ ----------
+ source : (object, attribute name) pair
+ target : (object, attribute name) pair
+ transform: callable (optional)
+ Data transformation between source and target.
+
+ Examples
+ --------
>>> c = directional_link((src, "value"), (tgt, "value"))
- >>> src.value = 5 # updates target objects
- >>> tgt.value = 6 # does not update source object
- """
- updating = False
-
- def __init__(self, source, target, transform=None):
- self._transform = transform if transform else lambda x: x
- _validate_link(source, target)
- self.source, self.target = source, target
+ >>> src.value = 5 # updates target objects
+ >>> tgt.value = 6 # does not update source object
+ """
+ updating = False
+
+ def __init__(self, source, target, transform=None):
+ self._transform = transform if transform else lambda x: x
+ _validate_link(source, target)
+ self.source, self.target = source, target
self.link()
def link(self):
- try:
+ try:
setattr(self.target[0], self.target[1],
self._transform(getattr(self.source[0], self.source[1])))
- finally:
- self.source[0].observe(self._update, names=self.source[1])
-
- @contextlib.contextmanager
- def _busy_updating(self):
- self.updating = True
- try:
- yield
- finally:
- self.updating = False
-
- def _update(self, change):
- if self.updating:
- return
- with self._busy_updating():
- setattr(self.target[0], self.target[1],
+ finally:
+ self.source[0].observe(self._update, names=self.source[1])
+
+ @contextlib.contextmanager
+ def _busy_updating(self):
+ self.updating = True
+ try:
+ yield
+ finally:
+ self.updating = False
+
+ def _update(self, change):
+ if self.updating:
+ return
+ with self._busy_updating():
+ setattr(self.target[0], self.target[1],
self._transform(change.new))
-
- def unlink(self):
- self.source[0].unobserve(self._update, names=self.source[1])
-
-dlink = directional_link
-
-
-#-----------------------------------------------------------------------------
+
+ def unlink(self):
+ self.source[0].unobserve(self._update, names=self.source[1])
+
+dlink = directional_link
+
+
+#-----------------------------------------------------------------------------
# Base Descriptor Class
-#-----------------------------------------------------------------------------
-
-
-class BaseDescriptor(object):
- """Base descriptor class
-
- Notes
- -----
+#-----------------------------------------------------------------------------
+
+
+class BaseDescriptor(object):
+ """Base descriptor class
+
+ Notes
+ -----
This implements Python's descriptor protocol.
-
- This class is the base class for all such descriptors. The
- only magic we use is a custom metaclass for the main :class:`HasTraits`
- class that does the following:
-
- 1. Sets the :attr:`name` attribute of every :class:`BaseDescriptor`
- instance in the class dict to the name of the attribute.
- 2. Sets the :attr:`this_class` attribute of every :class:`BaseDescriptor`
- instance in the class dict to the *class* that declared the trait.
- This is used by the :class:`This` trait to allow subclasses to
- accept superclasses for :class:`This` values.
- """
-
- name = None
- this_class = None
-
- def class_init(self, cls, name):
- """Part of the initialization which may depend on the underlying
- HasDescriptors class.
-
- It is typically overloaded for specific types.
-
- This method is called by :meth:`MetaHasDescriptors.__init__`
- passing the class (`cls`) and `name` under which the descriptor
- has been assigned.
- """
- self.this_class = cls
- self.name = name
-
+
+ This class is the base class for all such descriptors. The
+ only magic we use is a custom metaclass for the main :class:`HasTraits`
+ class that does the following:
+
+ 1. Sets the :attr:`name` attribute of every :class:`BaseDescriptor`
+ instance in the class dict to the name of the attribute.
+ 2. Sets the :attr:`this_class` attribute of every :class:`BaseDescriptor`
+ instance in the class dict to the *class* that declared the trait.
+ This is used by the :class:`This` trait to allow subclasses to
+ accept superclasses for :class:`This` values.
+ """
+
+ name = None
+ this_class = None
+
+ def class_init(self, cls, name):
+ """Part of the initialization which may depend on the underlying
+ HasDescriptors class.
+
+ It is typically overloaded for specific types.
+
+ This method is called by :meth:`MetaHasDescriptors.__init__`
+ passing the class (`cls`) and `name` under which the descriptor
+ has been assigned.
+ """
+ self.this_class = cls
+ self.name = name
+
def subclass_init(self, cls):
pass
- def instance_init(self, obj):
- """Part of the initialization which may depend on the underlying
- HasDescriptors instance.
-
- It is typically overloaded for specific types.
-
- This method is called by :meth:`HasTraits.__new__` and in the
- :meth:`BaseDescriptor.instance_init` method of descriptors holding
- other descriptors.
- """
- pass
-
-
-class TraitType(BaseDescriptor):
- """A base class for all trait types.
- """
-
- metadata = {}
- allow_none = False
- read_only = False
- info_text = 'any value'
+ def instance_init(self, obj):
+ """Part of the initialization which may depend on the underlying
+ HasDescriptors instance.
+
+ It is typically overloaded for specific types.
+
+ This method is called by :meth:`HasTraits.__new__` and in the
+ :meth:`BaseDescriptor.instance_init` method of descriptors holding
+ other descriptors.
+ """
+ pass
+
+
+class TraitType(BaseDescriptor):
+ """A base class for all trait types.
+ """
+
+ metadata = {}
+ allow_none = False
+ read_only = False
+ info_text = 'any value'
default_value = Undefined
-
+
def __init__(self, default_value=Undefined, allow_none=False, read_only=None, help=None,
config=None, **kwargs):
- """Declare a traitlet.
-
- If *allow_none* is True, None is a valid value in addition to any
- values that are normally valid. The default is up to the subclass.
- For most trait types, the default value for ``allow_none`` is False.
+ """Declare a traitlet.
+
+ If *allow_none* is True, None is a valid value in addition to any
+ values that are normally valid. The default is up to the subclass.
+ For most trait types, the default value for ``allow_none`` is False.
If *read_only* is True, attempts to directly modify a trait attribute raises a TraitError.
- Extra metadata can be associated with the traitlet using the .tag() convenience method
- or by using the traitlet instance's .metadata dictionary.
- """
- if default_value is not Undefined:
- self.default_value = default_value
+ Extra metadata can be associated with the traitlet using the .tag() convenience method
+ or by using the traitlet instance's .metadata dictionary.
+ """
+ if default_value is not Undefined:
+ self.default_value = default_value
if allow_none:
- self.allow_none = allow_none
- if read_only is not None:
- self.read_only = read_only
- self.help = help if help is not None else ''
-
+ self.allow_none = allow_none
+ if read_only is not None:
+ self.read_only = read_only
+ self.help = help if help is not None else ''
+
if len(kwargs) > 0:
- stacklevel = 1
- f = inspect.currentframe()
- # count supers to determine stacklevel for warning
- while f.f_code.co_name == '__init__':
- stacklevel += 1
- f = f.f_back
+ stacklevel = 1
+ f = inspect.currentframe()
+ # count supers to determine stacklevel for warning
+ while f.f_code.co_name == '__init__':
+ stacklevel += 1
+ f = f.f_back
mod = f.f_globals.get('__name__') or ''
pkg = mod.split('.', 1)[0]
key = tuple(['metadata-tag', pkg] + sorted(kwargs))
@@ -468,21 +468,21 @@ class TraitType(BaseDescriptor):
"With traitlets 4.1, metadata should be set using the .tag() method, "
"e.g., Int().tag(key1='value1', key2='value2')" % (kwargs,),
DeprecationWarning, stacklevel=stacklevel)
- if len(self.metadata) > 0:
- self.metadata = self.metadata.copy()
+ if len(self.metadata) > 0:
+ self.metadata = self.metadata.copy()
self.metadata.update(kwargs)
- else:
+ else:
self.metadata = kwargs
- else:
- self.metadata = self.metadata.copy()
+ else:
+ self.metadata = self.metadata.copy()
if config is not None:
self.metadata['config'] = config
-
- # We add help to the metadata during a deprecation period so that
- # code that looks for the help string there can find it.
- if help is not None:
- self.metadata['help'] = help
-
+
+ # We add help to the metadata during a deprecation period so that
+ # code that looks for the help string there can find it.
+ if help is not None:
+ self.metadata['help'] = help
+
def from_string(self, s):
"""Get a value from a config string
@@ -515,28 +515,28 @@ class TraitType(BaseDescriptor):
# Undefined will raise in TraitType.get
return self.default_value
- def get_default_value(self):
- """DEPRECATED: Retrieve the static default value for this trait.
- Use self.default_value instead
- """
+ def get_default_value(self):
+ """DEPRECATED: Retrieve the static default value for this trait.
+ Use self.default_value instead
+ """
warn("get_default_value is deprecated in traitlets 4.0: use the .default_value attribute", DeprecationWarning,
- stacklevel=2)
- return self.default_value
-
- def init_default_value(self, obj):
- """DEPRECATED: Set the static default value for the trait type.
- """
+ stacklevel=2)
+ return self.default_value
+
+ def init_default_value(self, obj):
+ """DEPRECATED: Set the static default value for the trait type.
+ """
warn("init_default_value is deprecated in traitlets 4.0, and may be removed in the future", DeprecationWarning,
- stacklevel=2)
- value = self._validate(obj, self.default_value)
- obj._trait_values[self.name] = value
- return value
-
+ stacklevel=2)
+ value = self._validate(obj, self.default_value)
+ obj._trait_values[self.name] = value
+ return value
+
def get(self, obj, cls=None):
- try:
- value = obj._trait_values[self.name]
- except KeyError:
- # Check for a dynamic initializer.
+ try:
+ value = obj._trait_values[self.name]
+ except KeyError:
+ # Check for a dynamic initializer.
default = obj.trait_defaults(self.name)
if default is Undefined:
warn(
@@ -548,93 +548,93 @@ class TraitType(BaseDescriptor):
)
with obj.cross_validation_lock:
value = self._validate(obj, default)
- obj._trait_values[self.name] = value
+ obj._trait_values[self.name] = value
obj._notify_observers(Bunch(
name=self.name,
value=value,
owner=obj,
type='default',
))
- return value
- except Exception:
- # This should never be reached.
- raise TraitError('Unexpected error in TraitType: '
- 'default value not set properly')
- else:
- return value
-
- def __get__(self, obj, cls=None):
- """Get the value of the trait by self.name for the instance.
-
- Default values are instantiated when :meth:`HasTraits.__new__`
- is called. Thus by the time this method gets called either the
- default value or a user defined value (they called :meth:`__set__`)
- is in the :class:`HasTraits` instance.
- """
- if obj is None:
- return self
- else:
- return self.get(obj, cls)
-
- def set(self, obj, value):
- new_value = self._validate(obj, value)
- try:
- old_value = obj._trait_values[self.name]
- except KeyError:
- old_value = self.default_value
-
- obj._trait_values[self.name] = new_value
- try:
- silent = bool(old_value == new_value)
+ return value
+ except Exception:
+ # This should never be reached.
+ raise TraitError('Unexpected error in TraitType: '
+ 'default value not set properly')
+ else:
+ return value
+
+ def __get__(self, obj, cls=None):
+ """Get the value of the trait by self.name for the instance.
+
+ Default values are instantiated when :meth:`HasTraits.__new__`
+ is called. Thus by the time this method gets called either the
+ default value or a user defined value (they called :meth:`__set__`)
+ is in the :class:`HasTraits` instance.
+ """
+ if obj is None:
+ return self
+ else:
+ return self.get(obj, cls)
+
+ def set(self, obj, value):
+ new_value = self._validate(obj, value)
+ try:
+ old_value = obj._trait_values[self.name]
+ except KeyError:
+ old_value = self.default_value
+
+ obj._trait_values[self.name] = new_value
+ try:
+ silent = bool(old_value == new_value)
except Exception:
- # if there is an error in comparing, default to notify
- silent = False
- if silent is not True:
- # we explicitly compare silent to True just in case the equality
- # comparison above returns something other than True/False
- obj._notify_trait(self.name, old_value, new_value)
-
- def __set__(self, obj, value):
- """Set the value of the trait by self.name for the instance.
-
- Values pass through a validation stage where errors are raised when
- impropper types, or types that cannot be coerced, are encountered.
- """
- if self.read_only:
- raise TraitError('The "%s" trait is read-only.' % self.name)
- else:
- self.set(obj, value)
-
- def _validate(self, obj, value):
- if value is None and self.allow_none:
- return value
- if hasattr(self, 'validate'):
- value = self.validate(obj, value)
- if obj._cross_validation_lock is False:
- value = self._cross_validate(obj, value)
- return value
-
- def _cross_validate(self, obj, value):
- if self.name in obj._trait_validators:
+ # if there is an error in comparing, default to notify
+ silent = False
+ if silent is not True:
+ # we explicitly compare silent to True just in case the equality
+ # comparison above returns something other than True/False
+ obj._notify_trait(self.name, old_value, new_value)
+
+ def __set__(self, obj, value):
+ """Set the value of the trait by self.name for the instance.
+
+ Values pass through a validation stage where errors are raised when
+ impropper types, or types that cannot be coerced, are encountered.
+ """
+ if self.read_only:
+ raise TraitError('The "%s" trait is read-only.' % self.name)
+ else:
+ self.set(obj, value)
+
+ def _validate(self, obj, value):
+ if value is None and self.allow_none:
+ return value
+ if hasattr(self, 'validate'):
+ value = self.validate(obj, value)
+ if obj._cross_validation_lock is False:
+ value = self._cross_validate(obj, value)
+ return value
+
+ def _cross_validate(self, obj, value):
+ if self.name in obj._trait_validators:
proposal = Bunch({'trait': self, 'value': value, 'owner': obj})
- value = obj._trait_validators[self.name](obj, proposal)
- elif hasattr(obj, '_%s_validate' % self.name):
- meth_name = '_%s_validate' % self.name
- cross_validate = getattr(obj, meth_name)
- _deprecated_method(cross_validate, obj.__class__, meth_name,
- "use @validate decorator instead.")
- value = cross_validate(value, self)
- return value
-
- def __or__(self, other):
- if isinstance(other, Union):
- return Union([self] + other.trait_types)
- else:
- return Union([self, other])
-
- def info(self):
- return self.info_text
-
+ value = obj._trait_validators[self.name](obj, proposal)
+ elif hasattr(obj, '_%s_validate' % self.name):
+ meth_name = '_%s_validate' % self.name
+ cross_validate = getattr(obj, meth_name)
+ _deprecated_method(cross_validate, obj.__class__, meth_name,
+ "use @validate decorator instead.")
+ value = cross_validate(value, self)
+ return value
+
+ def __or__(self, other):
+ if isinstance(other, Union):
+ return Union([self] + other.trait_types)
+ else:
+ return Union([self, other])
+
+ def info(self):
+ return self.info_text
+
def error(self, obj, value, error=None, info=None):
"""Raise a TraitError
@@ -676,7 +676,7 @@ class TraitType(BaseDescriptor):
"expected %s, not %s." % (self.name, chain,
error.args[1], describe("the", error.args[0])),)
raise error
- else:
+ else:
# this trait caused an error
if self.name is None:
# this is not the root trait
@@ -690,196 +690,196 @@ class TraitType(BaseDescriptor):
e = "The '%s' trait expected %s, not %s." % (
self.name, self.info(), describe("the", value))
raise TraitError(e)
-
- def get_metadata(self, key, default=None):
- """DEPRECATED: Get a metadata value.
-
- Use .metadata[key] or .metadata.get(key, default) instead.
- """
- if key == 'help':
- msg = "use the instance .help string directly, like x.help"
- else:
- msg = "use the instance .metadata dictionary directly, like x.metadata[key] or x.metadata.get(key, default)"
+
+ def get_metadata(self, key, default=None):
+ """DEPRECATED: Get a metadata value.
+
+ Use .metadata[key] or .metadata.get(key, default) instead.
+ """
+ if key == 'help':
+ msg = "use the instance .help string directly, like x.help"
+ else:
+ msg = "use the instance .metadata dictionary directly, like x.metadata[key] or x.metadata.get(key, default)"
warn("Deprecated in traitlets 4.1, " + msg, DeprecationWarning, stacklevel=2)
- return self.metadata.get(key, default)
-
- def set_metadata(self, key, value):
- """DEPRECATED: Set a metadata key/value.
-
- Use .metadata[key] = value instead.
- """
- if key == 'help':
- msg = "use the instance .help string directly, like x.help = value"
- else:
- msg = "use the instance .metadata dictionary directly, like x.metadata[key] = value"
+ return self.metadata.get(key, default)
+
+ def set_metadata(self, key, value):
+ """DEPRECATED: Set a metadata key/value.
+
+ Use .metadata[key] = value instead.
+ """
+ if key == 'help':
+ msg = "use the instance .help string directly, like x.help = value"
+ else:
+ msg = "use the instance .metadata dictionary directly, like x.metadata[key] = value"
warn("Deprecated in traitlets 4.1, " + msg, DeprecationWarning, stacklevel=2)
- self.metadata[key] = value
-
- def tag(self, **metadata):
- """Sets metadata and returns self.
-
- This allows convenient metadata tagging when initializing the trait, such as:
-
- >>> Int(0).tag(config=True, sync=True)
- """
+ self.metadata[key] = value
+
+ def tag(self, **metadata):
+ """Sets metadata and returns self.
+
+ This allows convenient metadata tagging when initializing the trait, such as:
+
+ >>> Int(0).tag(config=True, sync=True)
+ """
maybe_constructor_keywords = set(metadata.keys()).intersection({'help','allow_none', 'read_only', 'default_value'})
if maybe_constructor_keywords:
warn('The following attributes are set in using `tag`, but seem to be constructor keywords arguments: %s '%
maybe_constructor_keywords, UserWarning, stacklevel=2)
- self.metadata.update(metadata)
- return self
-
- def default_value_repr(self):
- return repr(self.default_value)
-
-#-----------------------------------------------------------------------------
-# The HasTraits implementation
-#-----------------------------------------------------------------------------
-
-class _CallbackWrapper(object):
- """An object adapting a on_trait_change callback into an observe callback.
-
- The comparison operator __eq__ is implemented to enable removal of wrapped
- callbacks.
- """
-
- def __init__(self, cb):
- self.cb = cb
- # Bound methods have an additional 'self' argument.
- offset = -1 if isinstance(self.cb, types.MethodType) else 0
- self.nargs = len(getargspec(cb)[0]) + offset
- if (self.nargs > 4):
- raise TraitError('a trait changed callback must have 0-4 arguments.')
-
- def __eq__(self, other):
- # The wrapper is equal to the wrapped element
- if isinstance(other, _CallbackWrapper):
- return self.cb == other.cb
- else:
- return self.cb == other
-
- def __call__(self, change):
- # The wrapper is callable
- if self.nargs == 0:
- self.cb()
- elif self.nargs == 1:
+ self.metadata.update(metadata)
+ return self
+
+ def default_value_repr(self):
+ return repr(self.default_value)
+
+#-----------------------------------------------------------------------------
+# The HasTraits implementation
+#-----------------------------------------------------------------------------
+
+class _CallbackWrapper(object):
+ """An object adapting a on_trait_change callback into an observe callback.
+
+ The comparison operator __eq__ is implemented to enable removal of wrapped
+ callbacks.
+ """
+
+ def __init__(self, cb):
+ self.cb = cb
+ # Bound methods have an additional 'self' argument.
+ offset = -1 if isinstance(self.cb, types.MethodType) else 0
+ self.nargs = len(getargspec(cb)[0]) + offset
+ if (self.nargs > 4):
+ raise TraitError('a trait changed callback must have 0-4 arguments.')
+
+ def __eq__(self, other):
+ # The wrapper is equal to the wrapped element
+ if isinstance(other, _CallbackWrapper):
+ return self.cb == other.cb
+ else:
+ return self.cb == other
+
+ def __call__(self, change):
+ # The wrapper is callable
+ if self.nargs == 0:
+ self.cb()
+ elif self.nargs == 1:
self.cb(change.name)
- elif self.nargs == 2:
+ elif self.nargs == 2:
self.cb(change.name, change.new)
- elif self.nargs == 3:
+ elif self.nargs == 3:
self.cb(change.name, change.old, change.new)
- elif self.nargs == 4:
+ elif self.nargs == 4:
self.cb(change.name, change.old, change.new, change.owner)
-
-def _callback_wrapper(cb):
- if isinstance(cb, _CallbackWrapper):
- return cb
- else:
- return _CallbackWrapper(cb)
-
-
-class MetaHasDescriptors(type):
- """A metaclass for HasDescriptors.
-
- This metaclass makes sure that any TraitType class attributes are
- instantiated and sets their name attribute.
- """
-
- def __new__(mcls, name, bases, classdict):
- """Create the HasDescriptors class."""
+
+def _callback_wrapper(cb):
+ if isinstance(cb, _CallbackWrapper):
+ return cb
+ else:
+ return _CallbackWrapper(cb)
+
+
+class MetaHasDescriptors(type):
+ """A metaclass for HasDescriptors.
+
+ This metaclass makes sure that any TraitType class attributes are
+ instantiated and sets their name attribute.
+ """
+
+ def __new__(mcls, name, bases, classdict):
+ """Create the HasDescriptors class."""
for k, v in classdict.items():
- # ----------------------------------------------------------------
- # Support of deprecated behavior allowing for TraitType types
- # to be used instead of TraitType instances.
- if inspect.isclass(v) and issubclass(v, TraitType):
+ # ----------------------------------------------------------------
+ # Support of deprecated behavior allowing for TraitType types
+ # to be used instead of TraitType instances.
+ if inspect.isclass(v) and issubclass(v, TraitType):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)."
" Passing types is deprecated in traitlets 4.1.",
- DeprecationWarning, stacklevel=2)
- classdict[k] = v()
- # ----------------------------------------------------------------
-
- return super(MetaHasDescriptors, mcls).__new__(mcls, name, bases, classdict)
-
- def __init__(cls, name, bases, classdict):
- """Finish initializing the HasDescriptors class."""
- super(MetaHasDescriptors, cls).__init__(name, bases, classdict)
- cls.setup_class(classdict)
-
- def setup_class(cls, classdict):
- """Setup descriptor instance on the class
-
- This sets the :attr:`this_class` and :attr:`name` attributes of each
- BaseDescriptor in the class dict of the newly created ``cls`` before
- calling their :attr:`class_init` method.
- """
+ DeprecationWarning, stacklevel=2)
+ classdict[k] = v()
+ # ----------------------------------------------------------------
+
+ return super(MetaHasDescriptors, mcls).__new__(mcls, name, bases, classdict)
+
+ def __init__(cls, name, bases, classdict):
+ """Finish initializing the HasDescriptors class."""
+ super(MetaHasDescriptors, cls).__init__(name, bases, classdict)
+ cls.setup_class(classdict)
+
+ def setup_class(cls, classdict):
+ """Setup descriptor instance on the class
+
+ This sets the :attr:`this_class` and :attr:`name` attributes of each
+ BaseDescriptor in the class dict of the newly created ``cls`` before
+ calling their :attr:`class_init` method.
+ """
for k, v in classdict.items():
- if isinstance(v, BaseDescriptor):
- v.class_init(cls, k)
-
+ if isinstance(v, BaseDescriptor):
+ v.class_init(cls, k)
+
for k, v in getmembers(cls):
if isinstance(v, BaseDescriptor):
v.subclass_init(cls)
-
-
-class MetaHasTraits(MetaHasDescriptors):
- """A metaclass for HasTraits."""
-
- def setup_class(cls, classdict):
- cls._trait_default_generators = {}
- super(MetaHasTraits, cls).setup_class(classdict)
-
-
+
+
+class MetaHasTraits(MetaHasDescriptors):
+ """A metaclass for HasTraits."""
+
+ def setup_class(cls, classdict):
+ cls._trait_default_generators = {}
+ super(MetaHasTraits, cls).setup_class(classdict)
+
+
def observe(*names, type="change"):
- """A decorator which can be used to observe Traits on a class.
-
+ """A decorator which can be used to observe Traits on a class.
+
The handler passed to the decorator will be called with one ``change``
dict argument. The change dictionary at least holds a 'type' key and a
'name' key, corresponding respectively to the type of notification and the
name of the attribute that triggered the notification.
-
- Other keys may be passed depending on the value of 'type'. In the case
- where type is 'change', we also have the following keys:
- * ``owner`` : the HasTraits instance
- * ``old`` : the old value of the modified trait attribute
- * ``new`` : the new value of the modified trait attribute
- * ``name`` : the name of the modified trait attribute.
-
- Parameters
- ----------
- *names
- The str names of the Traits to observe on the object.
+
+ Other keys may be passed depending on the value of 'type'. In the case
+ where type is 'change', we also have the following keys:
+ * ``owner`` : the HasTraits instance
+ * ``old`` : the old value of the modified trait attribute
+ * ``new`` : the new value of the modified trait attribute
+ * ``name`` : the name of the modified trait attribute.
+
+ Parameters
+ ----------
+ *names
+ The str names of the Traits to observe on the object.
type : str, kwarg-only
The type of event to observe (e.g. 'change')
- """
+ """
if not names:
raise TypeError("Please specify at least one trait name to observe.")
for name in names:
if name is not All and not isinstance(name, str):
raise TypeError("trait names to observe must be strings or All, not %r" % name)
return ObserveHandler(names, type=type)
-
-
-def observe_compat(func):
- """Backward-compatibility shim decorator for observers
-
- Use with:
-
- @observe('name')
- @observe_compat
- def _foo_changed(self, change):
- ...
-
- With this, `super()._foo_changed(self, name, old, new)` in subclasses will still work.
- Allows adoption of new observer API without breaking subclasses that override and super.
- """
- def compatible_observer(self, change_or_name, old=Undefined, new=Undefined):
- if isinstance(change_or_name, dict):
- change = change_or_name
- else:
- clsname = self.__class__.__name__
+
+
+def observe_compat(func):
+ """Backward-compatibility shim decorator for observers
+
+ Use with:
+
+ @observe('name')
+ @observe_compat
+ def _foo_changed(self, change):
+ ...
+
+ With this, `super()._foo_changed(self, name, old, new)` in subclasses will still work.
+ Allows adoption of new observer API without breaking subclasses that override and super.
+ """
+ def compatible_observer(self, change_or_name, old=Undefined, new=Undefined):
+ if isinstance(change_or_name, dict):
+ change = change_or_name
+ else:
+ clsname = self.__class__.__name__
warn("A parent of %s._%s_changed has adopted the new (traitlets 4.1) @observe(change) API" % (
- clsname, change_or_name), DeprecationWarning)
+ clsname, change_or_name), DeprecationWarning)
change = Bunch(
type='change',
old=old,
@@ -887,151 +887,151 @@ def observe_compat(func):
name=change_or_name,
owner=self,
)
- return func(self, change)
- return compatible_observer
-
-
-def validate(*names):
- """A decorator to register cross validator of HasTraits object's state
- when a Trait is set.
-
- The handler passed to the decorator must have one ``proposal`` dict argument.
- The proposal dictionary must hold the following keys:
-
- * ``owner`` : the HasTraits instance
- * ``value`` : the proposed value for the modified trait attribute
- * ``trait`` : the TraitType instance associated with the attribute
-
- Parameters
- ----------
+ return func(self, change)
+ return compatible_observer
+
+
+def validate(*names):
+ """A decorator to register cross validator of HasTraits object's state
+ when a Trait is set.
+
+ The handler passed to the decorator must have one ``proposal`` dict argument.
+ The proposal dictionary must hold the following keys:
+
+ * ``owner`` : the HasTraits instance
+ * ``value`` : the proposed value for the modified trait attribute
+ * ``trait`` : the TraitType instance associated with the attribute
+
+ Parameters
+ ----------
*names
- The str names of the Traits to validate.
-
- Notes
- -----
+ The str names of the Traits to validate.
+
+ Notes
+ -----
Since the owner has access to the ``HasTraits`` instance via the 'owner' key,
- the registered cross validator could potentially make changes to attributes
- of the ``HasTraits`` instance. However, we recommend not to do so. The reason
- is that the cross-validation of attributes may run in arbitrary order when
+ the registered cross validator could potentially make changes to attributes
+ of the ``HasTraits`` instance. However, we recommend not to do so. The reason
+ is that the cross-validation of attributes may run in arbitrary order when
exiting the ``hold_trait_notifications`` context, and such changes may not
- commute.
- """
+ commute.
+ """
if not names:
raise TypeError("Please specify at least one trait name to validate.")
for name in names:
if name is not All and not isinstance(name, str):
raise TypeError("trait names to validate must be strings or All, not %r" % name)
- return ValidateHandler(names)
-
-
-def default(name):
- """ A decorator which assigns a dynamic default for a Trait on a HasTraits object.
-
- Parameters
- ----------
- name
- The str name of the Trait on the object whose default should be generated.
-
- Notes
- -----
- Unlike observers and validators which are properties of the HasTraits
- instance, default value generators are class-level properties.
-
- Besides, default generators are only invoked if they are registered in
- subclasses of `this_type`.
-
- ::
-
- class A(HasTraits):
- bar = Int()
-
- @default('bar')
- def get_bar_default(self):
- return 11
-
- class B(A):
- bar = Float() # This trait ignores the default generator defined in
- # the base class A
-
- class C(B):
-
- @default('bar')
- def some_other_default(self): # This default generator should not be
- return 3.0 # ignored since it is defined in a
- # class derived from B.a.this_class.
- """
+ return ValidateHandler(names)
+
+
+def default(name):
+ """ A decorator which assigns a dynamic default for a Trait on a HasTraits object.
+
+ Parameters
+ ----------
+ name
+ The str name of the Trait on the object whose default should be generated.
+
+ Notes
+ -----
+ Unlike observers and validators which are properties of the HasTraits
+ instance, default value generators are class-level properties.
+
+ Besides, default generators are only invoked if they are registered in
+ subclasses of `this_type`.
+
+ ::
+
+ class A(HasTraits):
+ bar = Int()
+
+ @default('bar')
+ def get_bar_default(self):
+ return 11
+
+ class B(A):
+ bar = Float() # This trait ignores the default generator defined in
+ # the base class A
+
+ class C(B):
+
+ @default('bar')
+ def some_other_default(self): # This default generator should not be
+ return 3.0 # ignored since it is defined in a
+ # class derived from B.a.this_class.
+ """
if not isinstance(name, str):
raise TypeError("Trait name must be a string or All, not %r" % name)
- return DefaultHandler(name)
-
-
-class EventHandler(BaseDescriptor):
-
- def _init_call(self, func):
- self.func = func
- return self
-
- def __call__(self, *args, **kwargs):
+ return DefaultHandler(name)
+
+
+class EventHandler(BaseDescriptor):
+
+ def _init_call(self, func):
+ self.func = func
+ return self
+
+ def __call__(self, *args, **kwargs):
"""Pass `*args` and `**kwargs` to the handler's function if it exists."""
- if hasattr(self, 'func'):
- return self.func(*args, **kwargs)
- else:
- return self._init_call(*args, **kwargs)
-
- def __get__(self, inst, cls=None):
- if inst is None:
- return self
- return types.MethodType(self.func, inst)
-
-
-class ObserveHandler(EventHandler):
-
- def __init__(self, names, type):
- self.trait_names = names
- self.type = type
-
- def instance_init(self, inst):
- inst.observe(self, self.trait_names, type=self.type)
-
-
-class ValidateHandler(EventHandler):
-
- def __init__(self, names):
- self.trait_names = names
-
- def instance_init(self, inst):
- inst._register_validator(self, self.trait_names)
-
-
-class DefaultHandler(EventHandler):
-
- def __init__(self, name):
- self.trait_name = name
-
- def class_init(self, cls, name):
+ if hasattr(self, 'func'):
+ return self.func(*args, **kwargs)
+ else:
+ return self._init_call(*args, **kwargs)
+
+ def __get__(self, inst, cls=None):
+ if inst is None:
+ return self
+ return types.MethodType(self.func, inst)
+
+
+class ObserveHandler(EventHandler):
+
+ def __init__(self, names, type):
+ self.trait_names = names
+ self.type = type
+
+ def instance_init(self, inst):
+ inst.observe(self, self.trait_names, type=self.type)
+
+
+class ValidateHandler(EventHandler):
+
+ def __init__(self, names):
+ self.trait_names = names
+
+ def instance_init(self, inst):
+ inst._register_validator(self, self.trait_names)
+
+
+class DefaultHandler(EventHandler):
+
+ def __init__(self, name):
+ self.trait_name = name
+
+ def class_init(self, cls, name):
super().class_init(cls, name)
- cls._trait_default_generators[self.trait_name] = self
-
-
+ cls._trait_default_generators[self.trait_name] = self
+
+
class HasDescriptors(metaclass=MetaHasDescriptors):
- """The base class for all classes that have descriptors.
- """
-
+ """The base class for all classes that have descriptors.
+ """
+
def __new__(*args, **kwargs):
# Pass cls as args[0] to allow "cls" as keyword argument
cls = args[0]
args = args[1:]
- # This is needed because object.__new__ only accepts
- # the cls argument.
- new_meth = super(HasDescriptors, cls).__new__
- if new_meth is object.__new__:
- inst = new_meth(cls)
- else:
+ # This is needed because object.__new__ only accepts
+ # the cls argument.
+ new_meth = super(HasDescriptors, cls).__new__
+ if new_meth is object.__new__:
+ inst = new_meth(cls)
+ else:
inst = new_meth(cls, *args, **kwargs)
inst.setup_instance(*args, **kwargs)
- return inst
-
+ return inst
+
def setup_instance(*args, **kwargs):
"""
This is called **before** self.__init__ is called.
@@ -1041,39 +1041,39 @@ class HasDescriptors(metaclass=MetaHasDescriptors):
args = args[1:]
self._cross_validation_lock = False
- cls = self.__class__
- for key in dir(cls):
- # Some descriptors raise AttributeError like zope.interface's
- # __provides__ attributes even though they exist. This causes
- # AttributeErrors even though they are listed in dir(cls).
- try:
- value = getattr(cls, key)
- except AttributeError:
- pass
- else:
- if isinstance(value, BaseDescriptor):
- value.instance_init(self)
-
-
+ cls = self.__class__
+ for key in dir(cls):
+ # Some descriptors raise AttributeError like zope.interface's
+ # __provides__ attributes even though they exist. This causes
+ # AttributeErrors even though they are listed in dir(cls).
+ try:
+ value = getattr(cls, key)
+ except AttributeError:
+ pass
+ else:
+ if isinstance(value, BaseDescriptor):
+ value.instance_init(self)
+
+
class HasTraits(HasDescriptors, metaclass=MetaHasTraits):
-
+
def setup_instance(*args, **kwargs):
# Pass self as args[0] to allow "self" as keyword argument
self = args[0]
args = args[1:]
- self._trait_values = {}
- self._trait_notifiers = {}
- self._trait_validators = {}
+ self._trait_values = {}
+ self._trait_notifiers = {}
+ self._trait_validators = {}
super(HasTraits, self).setup_instance(*args, **kwargs)
-
+
def __init__(self, *args, **kwargs):
- # Allow trait values to be set using keyword arguments.
- # We need to use setattr for this to trigger validation and
- # notifications.
+ # Allow trait values to be set using keyword arguments.
+ # We need to use setattr for this to trigger validation and
+ # notifications.
super_args = args
super_kwargs = {}
- with self.hold_trait_notifications():
+ with self.hold_trait_notifications():
for key, value in kwargs.items():
if self.has_trait(key):
setattr(self, key, value)
@@ -1099,38 +1099,38 @@ class HasTraits(HasDescriptors, metaclass=MetaHasTraits):
DeprecationWarning,
stacklevel=2,
)
-
- def __getstate__(self):
- d = self.__dict__.copy()
- # event handlers stored on an instance are
- # expected to be reinstantiated during a
- # recall of instance_init during __setstate__
- d['_trait_notifiers'] = {}
- d['_trait_validators'] = {}
+
+ def __getstate__(self):
+ d = self.__dict__.copy()
+ # event handlers stored on an instance are
+ # expected to be reinstantiated during a
+ # recall of instance_init during __setstate__
+ d['_trait_notifiers'] = {}
+ d['_trait_validators'] = {}
d['_trait_values'] = self._trait_values.copy()
d['_cross_validation_lock'] = False # FIXME: raise if cloning locked!
- return d
-
- def __setstate__(self, state):
- self.__dict__ = state.copy()
-
- # event handlers are reassigned to self
- cls = self.__class__
- for key in dir(cls):
- # Some descriptors raise AttributeError like zope.interface's
- # __provides__ attributes even though they exist. This causes
- # AttributeErrors even though they are listed in dir(cls).
- try:
- value = getattr(cls, key)
- except AttributeError:
- pass
- else:
- if isinstance(value, EventHandler):
- value.instance_init(self)
-
+ return d
+
+ def __setstate__(self, state):
+ self.__dict__ = state.copy()
+
+ # event handlers are reassigned to self
+ cls = self.__class__
+ for key in dir(cls):
+ # Some descriptors raise AttributeError like zope.interface's
+ # __provides__ attributes even though they exist. This causes
+ # AttributeErrors even though they are listed in dir(cls).
+ try:
+ value = getattr(cls, key)
+ except AttributeError:
+ pass
+ else:
+ if isinstance(value, EventHandler):
+ value.instance_init(self)
+
@property
- @contextlib.contextmanager
+ @contextlib.contextmanager
def cross_validation_lock(self):
"""
A contextmanager for running a block with our cross validation lock set
@@ -1150,72 +1150,72 @@ class HasTraits(HasDescriptors, metaclass=MetaHasTraits):
self._cross_validation_lock = False
@contextlib.contextmanager
- def hold_trait_notifications(self):
- """Context manager for bundling trait change notifications and cross
- validation.
-
- Use this when doing multiple trait assignments (init, config), to avoid
- race conditions in trait notifiers requesting other trait values.
- All trait notifications will fire after all values have been assigned.
- """
+ def hold_trait_notifications(self):
+ """Context manager for bundling trait change notifications and cross
+ validation.
+
+ Use this when doing multiple trait assignments (init, config), to avoid
+ race conditions in trait notifiers requesting other trait values.
+ All trait notifications will fire after all values have been assigned.
+ """
if self._cross_validation_lock:
- yield
- return
- else:
- cache = {}
- notify_change = self.notify_change
-
- def compress(past_changes, change):
- """Merges the provided change with the last if possible."""
- if past_changes is None:
- return [change]
- else:
+ yield
+ return
+ else:
+ cache = {}
+ notify_change = self.notify_change
+
+ def compress(past_changes, change):
+ """Merges the provided change with the last if possible."""
+ if past_changes is None:
+ return [change]
+ else:
if past_changes[-1]['type'] == 'change' and change.type == 'change':
past_changes[-1]['new'] = change.new
- else:
- # In case of changes other than 'change', append the notification.
- past_changes.append(change)
- return past_changes
-
- def hold(change):
+ else:
+ # In case of changes other than 'change', append the notification.
+ past_changes.append(change)
+ return past_changes
+
+ def hold(change):
name = change.name
- cache[name] = compress(cache.get(name), change)
-
- try:
- # Replace notify_change with `hold`, caching and compressing
- # notifications, disable cross validation and yield.
- self.notify_change = hold
- self._cross_validation_lock = True
- yield
- # Cross validate final values when context is released.
- for name in list(cache.keys()):
- trait = getattr(self.__class__, name)
- value = trait._cross_validate(self, getattr(self, name))
+ cache[name] = compress(cache.get(name), change)
+
+ try:
+ # Replace notify_change with `hold`, caching and compressing
+ # notifications, disable cross validation and yield.
+ self.notify_change = hold
+ self._cross_validation_lock = True
+ yield
+ # Cross validate final values when context is released.
+ for name in list(cache.keys()):
+ trait = getattr(self.__class__, name)
+ value = trait._cross_validate(self, getattr(self, name))
self.set_trait(name, value)
- except TraitError as e:
- # Roll back in case of TraitError during final cross validation.
- self.notify_change = lambda x: None
- for name, changes in cache.items():
- for change in changes[::-1]:
- # TODO: Separate in a rollback function per notification type.
+ except TraitError as e:
+ # Roll back in case of TraitError during final cross validation.
+ self.notify_change = lambda x: None
+ for name, changes in cache.items():
+ for change in changes[::-1]:
+ # TODO: Separate in a rollback function per notification type.
if change.type == 'change':
if change.old is not Undefined:
self.set_trait(name, change.old)
- else:
- self._trait_values.pop(name)
- cache = {}
- raise e
- finally:
- self._cross_validation_lock = False
+ else:
+ self._trait_values.pop(name)
+ cache = {}
+ raise e
+ finally:
+ self._cross_validation_lock = False
# Restore method retrieval from class
del self.notify_change
-
- # trigger delayed notifications
- for changes in cache.values():
- for change in changes:
- self.notify_change(change)
-
- def _notify_trait(self, name, old_value, new_value):
+
+ # trigger delayed notifications
+ for changes in cache.values():
+ for change in changes:
+ self.notify_change(change)
+
+ def _notify_trait(self, name, old_value, new_value):
self.notify_change(Bunch(
name=name,
old=old_value,
@@ -1223,8 +1223,8 @@ class HasTraits(HasDescriptors, metaclass=MetaHasTraits):
owner=self,
type='change',
))
-
- def notify_change(self, change):
+
+ def notify_change(self, change):
"""Notify observers of a change event"""
return self._notify_observers(change)
@@ -1234,188 +1234,188 @@ class HasTraits(HasDescriptors, metaclass=MetaHasTraits):
# cast to bunch if given a dict
event = Bunch(event)
name, type = event.name, event.type
-
- callables = []
- callables.extend(self._trait_notifiers.get(name, {}).get(type, []))
- callables.extend(self._trait_notifiers.get(name, {}).get(All, []))
- callables.extend(self._trait_notifiers.get(All, {}).get(type, []))
- callables.extend(self._trait_notifiers.get(All, {}).get(All, []))
-
- # Now static ones
- magic_name = '_%s_changed' % name
+
+ callables = []
+ callables.extend(self._trait_notifiers.get(name, {}).get(type, []))
+ callables.extend(self._trait_notifiers.get(name, {}).get(All, []))
+ callables.extend(self._trait_notifiers.get(All, {}).get(type, []))
+ callables.extend(self._trait_notifiers.get(All, {}).get(All, []))
+
+ # Now static ones
+ magic_name = '_%s_changed' % name
if event.type == "change" and hasattr(self, magic_name):
- class_value = getattr(self.__class__, magic_name)
- if not isinstance(class_value, ObserveHandler):
- _deprecated_method(class_value, self.__class__, magic_name,
- "use @observe and @unobserve instead.")
- cb = getattr(self, magic_name)
- # Only append the magic method if it was not manually registered
- if cb not in callables:
- callables.append(_callback_wrapper(cb))
-
- # Call them all now
- # Traits catches and logs errors here. I allow them to raise
- for c in callables:
- # Bound methods have an additional 'self' argument.
-
- if isinstance(c, _CallbackWrapper):
- c = c.__call__
+ class_value = getattr(self.__class__, magic_name)
+ if not isinstance(class_value, ObserveHandler):
+ _deprecated_method(class_value, self.__class__, magic_name,
+ "use @observe and @unobserve instead.")
+ cb = getattr(self, magic_name)
+ # Only append the magic method if it was not manually registered
+ if cb not in callables:
+ callables.append(_callback_wrapper(cb))
+
+ # Call them all now
+ # Traits catches and logs errors here. I allow them to raise
+ for c in callables:
+ # Bound methods have an additional 'self' argument.
+
+ if isinstance(c, _CallbackWrapper):
+ c = c.__call__
elif isinstance(c, EventHandler) and c.name is not None:
- c = getattr(self, c.name)
+ c = getattr(self, c.name)
c(event)
-
- def _add_notifiers(self, handler, name, type):
- if name not in self._trait_notifiers:
- nlist = []
- self._trait_notifiers[name] = {type: nlist}
- else:
- if type not in self._trait_notifiers[name]:
- nlist = []
- self._trait_notifiers[name][type] = nlist
- else:
- nlist = self._trait_notifiers[name][type]
- if handler not in nlist:
- nlist.append(handler)
-
- def _remove_notifiers(self, handler, name, type):
- try:
- if handler is None:
- del self._trait_notifiers[name][type]
- else:
- self._trait_notifiers[name][type].remove(handler)
- except KeyError:
- pass
-
- def on_trait_change(self, handler=None, name=None, remove=False):
- """DEPRECATED: Setup a handler to be called when a trait changes.
-
- This is used to setup dynamic notifications of trait changes.
-
- Static handlers can be created by creating methods on a HasTraits
- subclass with the naming convention '_[traitname]_changed'. Thus,
- to create static handler for the trait 'a', create the method
- _a_changed(self, name, old, new) (fewer arguments can be used, see
- below).
-
- If `remove` is True and `handler` is not specified, all change
- handlers for the specified name are uninstalled.
-
- Parameters
- ----------
- handler : callable, None
- A callable that is called when a trait changes. Its
- signature can be handler(), handler(name), handler(name, new),
- handler(name, old, new), or handler(name, old, new, self).
- name : list, str, None
- If None, the handler will apply to all traits. If a list
- of str, handler will apply to all names in the list. If a
- str, the handler will apply just to that name.
- remove : bool
- If False (the default), then install the handler. If True
- then unintall it.
- """
+
+ def _add_notifiers(self, handler, name, type):
+ if name not in self._trait_notifiers:
+ nlist = []
+ self._trait_notifiers[name] = {type: nlist}
+ else:
+ if type not in self._trait_notifiers[name]:
+ nlist = []
+ self._trait_notifiers[name][type] = nlist
+ else:
+ nlist = self._trait_notifiers[name][type]
+ if handler not in nlist:
+ nlist.append(handler)
+
+ def _remove_notifiers(self, handler, name, type):
+ try:
+ if handler is None:
+ del self._trait_notifiers[name][type]
+ else:
+ self._trait_notifiers[name][type].remove(handler)
+ except KeyError:
+ pass
+
+ def on_trait_change(self, handler=None, name=None, remove=False):
+ """DEPRECATED: Setup a handler to be called when a trait changes.
+
+ This is used to setup dynamic notifications of trait changes.
+
+ Static handlers can be created by creating methods on a HasTraits
+ subclass with the naming convention '_[traitname]_changed'. Thus,
+ to create static handler for the trait 'a', create the method
+ _a_changed(self, name, old, new) (fewer arguments can be used, see
+ below).
+
+ If `remove` is True and `handler` is not specified, all change
+ handlers for the specified name are uninstalled.
+
+ Parameters
+ ----------
+ handler : callable, None
+ A callable that is called when a trait changes. Its
+ signature can be handler(), handler(name), handler(name, new),
+ handler(name, old, new), or handler(name, old, new, self).
+ name : list, str, None
+ If None, the handler will apply to all traits. If a list
+ of str, handler will apply to all names in the list. If a
+ str, the handler will apply just to that name.
+ remove : bool
+ If False (the default), then install the handler. If True
+ then unintall it.
+ """
warn("on_trait_change is deprecated in traitlets 4.1: use observe instead",
- DeprecationWarning, stacklevel=2)
- if name is None:
- name = All
- if remove:
- self.unobserve(_callback_wrapper(handler), names=name)
- else:
- self.observe(_callback_wrapper(handler), names=name)
-
- def observe(self, handler, names=All, type='change'):
- """Setup a handler to be called when a trait changes.
-
- This is used to setup dynamic notifications of trait changes.
-
- Parameters
- ----------
- handler : callable
- A callable that is called when a trait changes. Its
+ DeprecationWarning, stacklevel=2)
+ if name is None:
+ name = All
+ if remove:
+ self.unobserve(_callback_wrapper(handler), names=name)
+ else:
+ self.observe(_callback_wrapper(handler), names=name)
+
+ def observe(self, handler, names=All, type='change'):
+ """Setup a handler to be called when a trait changes.
+
+ This is used to setup dynamic notifications of trait changes.
+
+ Parameters
+ ----------
+ handler : callable
+ A callable that is called when a trait changes. Its
signature should be ``handler(change)``, where ``change`` is a
dictionary. The change dictionary at least holds a 'type' key.
- * ``type``: the type of notification.
- Other keys may be passed depending on the value of 'type'. In the
- case where type is 'change', we also have the following keys:
- * ``owner`` : the HasTraits instance
- * ``old`` : the old value of the modified trait attribute
- * ``new`` : the new value of the modified trait attribute
- * ``name`` : the name of the modified trait attribute.
- names : list, str, All
- If names is All, the handler will apply to all traits. If a list
- of str, handler will apply to all names in the list. If a
- str, the handler will apply just to that name.
- type : str, All (default: 'change')
- The type of notification to filter by. If equal to All, then all
- notifications are passed to the observe handler.
- """
- names = parse_notifier_name(names)
- for n in names:
- self._add_notifiers(handler, n, type)
-
- def unobserve(self, handler, names=All, type='change'):
- """Remove a trait change handler.
-
+ * ``type``: the type of notification.
+ Other keys may be passed depending on the value of 'type'. In the
+ case where type is 'change', we also have the following keys:
+ * ``owner`` : the HasTraits instance
+ * ``old`` : the old value of the modified trait attribute
+ * ``new`` : the new value of the modified trait attribute
+ * ``name`` : the name of the modified trait attribute.
+ names : list, str, All
+ If names is All, the handler will apply to all traits. If a list
+ of str, handler will apply to all names in the list. If a
+ str, the handler will apply just to that name.
+ type : str, All (default: 'change')
+ The type of notification to filter by. If equal to All, then all
+ notifications are passed to the observe handler.
+ """
+ names = parse_notifier_name(names)
+ for n in names:
+ self._add_notifiers(handler, n, type)
+
+ def unobserve(self, handler, names=All, type='change'):
+ """Remove a trait change handler.
+
This is used to unregister handlers to trait change notifications.
-
- Parameters
- ----------
- handler : callable
- The callable called when a trait attribute changes.
- names : list, str, All (default: All)
- The names of the traits for which the specified handler should be
- uninstalled. If names is All, the specified handler is uninstalled
- from the list of notifiers corresponding to all changes.
- type : str or All (default: 'change')
- The type of notification to filter by. If All, the specified handler
- is uninstalled from the list of notifiers corresponding to all types.
- """
- names = parse_notifier_name(names)
- for n in names:
- self._remove_notifiers(handler, n, type)
-
- def unobserve_all(self, name=All):
- """Remove trait change handlers of any type for the specified name.
- If name is not specified, removes all trait notifiers."""
- if name is All:
- self._trait_notifiers = {}
- else:
- try:
- del self._trait_notifiers[name]
- except KeyError:
- pass
-
- def _register_validator(self, handler, names):
+
+ Parameters
+ ----------
+ handler : callable
+ The callable called when a trait attribute changes.
+ names : list, str, All (default: All)
+ The names of the traits for which the specified handler should be
+ uninstalled. If names is All, the specified handler is uninstalled
+ from the list of notifiers corresponding to all changes.
+ type : str or All (default: 'change')
+ The type of notification to filter by. If All, the specified handler
+ is uninstalled from the list of notifiers corresponding to all types.
+ """
+ names = parse_notifier_name(names)
+ for n in names:
+ self._remove_notifiers(handler, n, type)
+
+ def unobserve_all(self, name=All):
+ """Remove trait change handlers of any type for the specified name.
+ If name is not specified, removes all trait notifiers."""
+ if name is All:
+ self._trait_notifiers = {}
+ else:
+ try:
+ del self._trait_notifiers[name]
+ except KeyError:
+ pass
+
+ def _register_validator(self, handler, names):
"""Setup a handler to be called when a trait should be cross validated.
-
- This is used to setup dynamic notifications for cross-validation.
-
- If a validator is already registered for any of the provided names, a
+
+ This is used to setup dynamic notifications for cross-validation.
+
+ If a validator is already registered for any of the provided names, a
TraitError is raised and no new validator is registered.
-
- Parameters
- ----------
- handler : callable
- A callable that is called when the given trait is cross-validated.
+
+ Parameters
+ ----------
+ handler : callable
+ A callable that is called when the given trait is cross-validated.
Its signature is handler(proposal), where proposal is a Bunch (dictionary with attribute access)
with the following attributes/keys:
- * ``owner`` : the HasTraits instance
- * ``value`` : the proposed value for the modified trait attribute
- * ``trait`` : the TraitType instance associated with the attribute
- names : List of strings
- The names of the traits that should be cross-validated
- """
- for name in names:
- magic_name = '_%s_validate' % name
- if hasattr(self, magic_name):
- class_value = getattr(self.__class__, magic_name)
- if not isinstance(class_value, ValidateHandler):
+ * ``owner`` : the HasTraits instance
+ * ``value`` : the proposed value for the modified trait attribute
+ * ``trait`` : the TraitType instance associated with the attribute
+ names : List of strings
+ The names of the traits that should be cross-validated
+ """
+ for name in names:
+ magic_name = '_%s_validate' % name
+ if hasattr(self, magic_name):
+ class_value = getattr(self.__class__, magic_name)
+ if not isinstance(class_value, ValidateHandler):
_deprecated_method(class_value, self.__class__, magic_name,
- "use @validate decorator instead.")
- for name in names:
- self._trait_validators[name] = handler
-
+ "use @validate decorator instead.")
+ for name in names:
+ self._trait_validators[name] = handler
+
def add_traits(self, **traits):
"""Dynamically add trait attributes to the HasTraits instance."""
cls = self.__class__
@@ -1437,63 +1437,63 @@ class HasTraits(HasDescriptors, metaclass=MetaHasTraits):
else:
getattr(cls, name).set(self, value)
- @classmethod
- def class_trait_names(cls, **metadata):
- """Get a list of all the names of this class' traits.
-
- This method is just like the :meth:`trait_names` method,
- but is unbound.
- """
+ @classmethod
+ def class_trait_names(cls, **metadata):
+ """Get a list of all the names of this class' traits.
+
+ This method is just like the :meth:`trait_names` method,
+ but is unbound.
+ """
return list(cls.class_traits(**metadata))
-
- @classmethod
- def class_traits(cls, **metadata):
- """Get a ``dict`` of all the traits of this class. The dictionary
- is keyed on the name and the values are the TraitType objects.
-
- This method is just like the :meth:`traits` method, but is unbound.
-
- The TraitTypes returned don't know anything about the values
- that the various HasTrait's instances are holding.
-
- The metadata kwargs allow functions to be passed in which
- filter traits based on metadata values. The functions should
- take a single value as an argument and return a boolean. If
- any function returns False, then the trait is not included in
- the output. If a metadata key doesn't exist, None will be passed
- to the function.
- """
- traits = dict([memb for memb in getmembers(cls) if
- isinstance(memb[1], TraitType)])
-
- if len(metadata) == 0:
- return traits
-
- result = {}
- for name, trait in traits.items():
- for meta_name, meta_eval in metadata.items():
+
+ @classmethod
+ def class_traits(cls, **metadata):
+ """Get a ``dict`` of all the traits of this class. The dictionary
+ is keyed on the name and the values are the TraitType objects.
+
+ This method is just like the :meth:`traits` method, but is unbound.
+
+ The TraitTypes returned don't know anything about the values
+ that the various HasTrait's instances are holding.
+
+ The metadata kwargs allow functions to be passed in which
+ filter traits based on metadata values. The functions should
+ take a single value as an argument and return a boolean. If
+ any function returns False, then the trait is not included in
+ the output. If a metadata key doesn't exist, None will be passed
+ to the function.
+ """
+ traits = dict([memb for memb in getmembers(cls) if
+ isinstance(memb[1], TraitType)])
+
+ if len(metadata) == 0:
+ return traits
+
+ result = {}
+ for name, trait in traits.items():
+ for meta_name, meta_eval in metadata.items():
if not callable(meta_eval):
- meta_eval = _SimpleTest(meta_eval)
- if not meta_eval(trait.metadata.get(meta_name, None)):
- break
- else:
- result[name] = trait
-
- return result
-
- @classmethod
- def class_own_traits(cls, **metadata):
- """Get a dict of all the traitlets defined on this class, not a parent.
-
- Works like `class_traits`, except for excluding traits from parents.
- """
- sup = super(cls, cls)
- return {n: t for (n, t) in cls.class_traits(**metadata).items()
- if getattr(sup, n, None) is not t}
-
- def has_trait(self, name):
- """Returns True if the object has a trait with the specified name."""
- return isinstance(getattr(self.__class__, name, None), TraitType)
+ meta_eval = _SimpleTest(meta_eval)
+ if not meta_eval(trait.metadata.get(meta_name, None)):
+ break
+ else:
+ result[name] = trait
+
+ return result
+
+ @classmethod
+ def class_own_traits(cls, **metadata):
+ """Get a dict of all the traitlets defined on this class, not a parent.
+
+ Works like `class_traits`, except for excluding traits from parents.
+ """
+ sup = super(cls, cls)
+ return {n: t for (n, t) in cls.class_traits(**metadata).items()
+ if getattr(sup, n, None) is not t}
+
+ def has_trait(self, name):
+ """Returns True if the object has a trait with the specified name."""
+ return isinstance(getattr(self.__class__, name, None), TraitType)
def trait_has_value(self, name):
"""Returns True if the specified trait has a value.
@@ -1587,65 +1587,65 @@ class HasTraits(HasDescriptors, metaclass=MetaHasTraits):
defaults[n] = self._get_trait_default_generator(n)(self)
return defaults
- def trait_names(self, **metadata):
- """Get a list of all the names of this class' traits."""
+ def trait_names(self, **metadata):
+ """Get a list of all the names of this class' traits."""
return list(self.traits(**metadata))
-
- def traits(self, **metadata):
- """Get a ``dict`` of all the traits of this class. The dictionary
- is keyed on the name and the values are the TraitType objects.
-
- The TraitTypes returned don't know anything about the values
- that the various HasTrait's instances are holding.
-
- The metadata kwargs allow functions to be passed in which
- filter traits based on metadata values. The functions should
- take a single value as an argument and return a boolean. If
- any function returns False, then the trait is not included in
- the output. If a metadata key doesn't exist, None will be passed
- to the function.
- """
- traits = dict([memb for memb in getmembers(self.__class__) if
- isinstance(memb[1], TraitType)])
-
- if len(metadata) == 0:
- return traits
-
- result = {}
- for name, trait in traits.items():
- for meta_name, meta_eval in metadata.items():
+
+ def traits(self, **metadata):
+ """Get a ``dict`` of all the traits of this class. The dictionary
+ is keyed on the name and the values are the TraitType objects.
+
+ The TraitTypes returned don't know anything about the values
+ that the various HasTrait's instances are holding.
+
+ The metadata kwargs allow functions to be passed in which
+ filter traits based on metadata values. The functions should
+ take a single value as an argument and return a boolean. If
+ any function returns False, then the trait is not included in
+ the output. If a metadata key doesn't exist, None will be passed
+ to the function.
+ """
+ traits = dict([memb for memb in getmembers(self.__class__) if
+ isinstance(memb[1], TraitType)])
+
+ if len(metadata) == 0:
+ return traits
+
+ result = {}
+ for name, trait in traits.items():
+ for meta_name, meta_eval in metadata.items():
if not callable(meta_eval):
- meta_eval = _SimpleTest(meta_eval)
- if not meta_eval(trait.metadata.get(meta_name, None)):
- break
- else:
- result[name] = trait
-
- return result
-
- def trait_metadata(self, traitname, key, default=None):
- """Get metadata values for trait by key."""
- try:
- trait = getattr(self.__class__, traitname)
- except AttributeError:
- raise TraitError("Class %s does not have a trait named %s" %
- (self.__class__.__name__, traitname))
+ meta_eval = _SimpleTest(meta_eval)
+ if not meta_eval(trait.metadata.get(meta_name, None)):
+ break
+ else:
+ result[name] = trait
+
+ return result
+
+ def trait_metadata(self, traitname, key, default=None):
+ """Get metadata values for trait by key."""
+ try:
+ trait = getattr(self.__class__, traitname)
+ except AttributeError:
+ raise TraitError("Class %s does not have a trait named %s" %
+ (self.__class__.__name__, traitname))
metadata_name = '_' + traitname + '_metadata'
if hasattr(self, metadata_name) and key in getattr(self, metadata_name):
return getattr(self, metadata_name).get(key, default)
- else:
- return trait.metadata.get(key, default)
-
+ else:
+ return trait.metadata.get(key, default)
+
@classmethod
def class_own_trait_events(cls, name):
"""Get a dict of all event handlers defined on this class, not a parent.
-
+
Works like ``event_handlers``, except for excluding traits from parents.
"""
sup = super(cls, cls)
return {n: e for (n, e) in cls.events(name).items()
if getattr(sup, n, None) is not e}
-
+
@classmethod
def trait_events(cls, name=None):
"""Get a ``dict`` of all the event handlers of this class.
@@ -1672,288 +1672,288 @@ class HasTraits(HasDescriptors, metaclass=MetaHasTraits):
events[k] = v
return events
-#-----------------------------------------------------------------------------
-# Actual TraitTypes implementations/subclasses
-#-----------------------------------------------------------------------------
-
-#-----------------------------------------------------------------------------
-# TraitTypes subclasses for handling classes and instances of classes
-#-----------------------------------------------------------------------------
-
-
-class ClassBasedTraitType(TraitType):
- """
- A trait with error reporting and string -> type resolution for Type,
- Instance and This.
- """
-
- def _resolve_string(self, string):
- """
- Resolve a string supplied for a type into an actual object.
- """
- return import_item(string)
-
-
-class Type(ClassBasedTraitType):
- """A trait whose value must be a subclass of a specified class."""
-
+#-----------------------------------------------------------------------------
+# Actual TraitTypes implementations/subclasses
+#-----------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------
+# TraitTypes subclasses for handling classes and instances of classes
+#-----------------------------------------------------------------------------
+
+
+class ClassBasedTraitType(TraitType):
+ """
+ A trait with error reporting and string -> type resolution for Type,
+ Instance and This.
+ """
+
+ def _resolve_string(self, string):
+ """
+ Resolve a string supplied for a type into an actual object.
+ """
+ return import_item(string)
+
+
+class Type(ClassBasedTraitType):
+ """A trait whose value must be a subclass of a specified class."""
+
def __init__(self, default_value=Undefined, klass=None, **kwargs):
- """Construct a Type trait
-
- A Type trait specifies that its values must be subclasses of
- a particular class.
-
- If only ``default_value`` is given, it is used for the ``klass`` as
- well. If neither are given, both default to ``object``.
-
- Parameters
- ----------
- default_value : class, str or None
- The default value must be a subclass of klass. If an str,
- the str must be a fully specified class name, like 'foo.bar.Bah'.
- The string is resolved into real class, when the parent
- :class:`HasTraits` class is instantiated.
- klass : class, str [ default object ]
- Values of this trait must be a subclass of klass. The klass
- may be specified in a string like: 'foo.bar.MyClass'.
- The string is resolved into real class, when the parent
- :class:`HasTraits` class is instantiated.
- allow_none : bool [ default False ]
- Indicates whether None is allowed as an assignable value.
+ """Construct a Type trait
+
+ A Type trait specifies that its values must be subclasses of
+ a particular class.
+
+ If only ``default_value`` is given, it is used for the ``klass`` as
+ well. If neither are given, both default to ``object``.
+
+ Parameters
+ ----------
+ default_value : class, str or None
+ The default value must be a subclass of klass. If an str,
+ the str must be a fully specified class name, like 'foo.bar.Bah'.
+ The string is resolved into real class, when the parent
+ :class:`HasTraits` class is instantiated.
+ klass : class, str [ default object ]
+ Values of this trait must be a subclass of klass. The klass
+ may be specified in a string like: 'foo.bar.MyClass'.
+ The string is resolved into real class, when the parent
+ :class:`HasTraits` class is instantiated.
+ allow_none : bool [ default False ]
+ Indicates whether None is allowed as an assignable value.
**kwargs
extra kwargs passed to `ClassBasedTraitType`
- """
- if default_value is Undefined:
- new_default_value = object if (klass is None) else klass
- else:
- new_default_value = default_value
-
- if klass is None:
- if (default_value is None) or (default_value is Undefined):
- klass = object
- else:
- klass = default_value
-
+ """
+ if default_value is Undefined:
+ new_default_value = object if (klass is None) else klass
+ else:
+ new_default_value = default_value
+
+ if klass is None:
+ if (default_value is None) or (default_value is Undefined):
+ klass = object
+ else:
+ klass = default_value
+
if not (inspect.isclass(klass) or isinstance(klass, str)):
- raise TraitError("A Type trait must specify a class.")
-
- self.klass = klass
-
+ raise TraitError("A Type trait must specify a class.")
+
+ self.klass = klass
+
super().__init__(new_default_value, **kwargs)
-
- def validate(self, obj, value):
- """Validates that the value is a valid object instance."""
+
+ def validate(self, obj, value):
+ """Validates that the value is a valid object instance."""
if isinstance(value, str):
- try:
- value = self._resolve_string(value)
- except ImportError:
- raise TraitError("The '%s' trait of %s instance must be a type, but "
- "%r could not be imported" % (self.name, obj, value))
- try:
- if issubclass(value, self.klass):
- return value
+ try:
+ value = self._resolve_string(value)
+ except ImportError:
+ raise TraitError("The '%s' trait of %s instance must be a type, but "
+ "%r could not be imported" % (self.name, obj, value))
+ try:
+ if issubclass(value, self.klass):
+ return value
except Exception:
- pass
-
- self.error(obj, value)
-
- def info(self):
- """ Returns a description of the trait."""
+ pass
+
+ self.error(obj, value)
+
+ def info(self):
+ """ Returns a description of the trait."""
if isinstance(self.klass, str):
- klass = self.klass
- else:
+ klass = self.klass
+ else:
klass = self.klass.__module__ + '.' + self.klass.__name__
- result = "a subclass of '%s'" % klass
- if self.allow_none:
- return result + ' or None'
- return result
-
- def instance_init(self, obj):
- self._resolve_classes()
+ result = "a subclass of '%s'" % klass
+ if self.allow_none:
+ return result + ' or None'
+ return result
+
+ def instance_init(self, obj):
+ self._resolve_classes()
super().instance_init(obj)
-
- def _resolve_classes(self):
+
+ def _resolve_classes(self):
if isinstance(self.klass, str):
- self.klass = self._resolve_string(self.klass)
+ self.klass = self._resolve_string(self.klass)
if isinstance(self.default_value, str):
- self.default_value = self._resolve_string(self.default_value)
-
- def default_value_repr(self):
- value = self.default_value
+ self.default_value = self._resolve_string(self.default_value)
+
+ def default_value_repr(self):
+ value = self.default_value
if isinstance(value, str):
- return repr(value)
- else:
+ return repr(value)
+ else:
return repr(f'{value.__module__}.{value.__name__}')
-
-
-class Instance(ClassBasedTraitType):
- """A trait whose value must be an instance of a specified class.
-
- The value can also be an instance of a subclass of the specified class.
-
- Subclasses can declare default classes by overriding the klass attribute
- """
-
- klass = None
-
+
+
+class Instance(ClassBasedTraitType):
+ """A trait whose value must be an instance of a specified class.
+
+ The value can also be an instance of a subclass of the specified class.
+
+ Subclasses can declare default classes by overriding the klass attribute
+ """
+
+ klass = None
+
def __init__(self, klass=None, args=None, kw=None, **kwargs):
- """Construct an Instance trait.
-
- This trait allows values that are instances of a particular
- class or its subclasses. Our implementation is quite different
- from that of enthough.traits as we don't allow instances to be used
- for klass and we handle the ``args`` and ``kw`` arguments differently.
-
- Parameters
- ----------
- klass : class, str
- The class that forms the basis for the trait. Class names
- can also be specified as strings, like 'foo.bar.Bar'.
- args : tuple
- Positional arguments for generating the default value.
- kw : dict
- Keyword arguments for generating the default value.
- allow_none : bool [ default False ]
- Indicates whether None is allowed as a value.
+ """Construct an Instance trait.
+
+ This trait allows values that are instances of a particular
+ class or its subclasses. Our implementation is quite different
+ from that of enthough.traits as we don't allow instances to be used
+ for klass and we handle the ``args`` and ``kw`` arguments differently.
+
+ Parameters
+ ----------
+ klass : class, str
+ The class that forms the basis for the trait. Class names
+ can also be specified as strings, like 'foo.bar.Bar'.
+ args : tuple
+ Positional arguments for generating the default value.
+ kw : dict
+ Keyword arguments for generating the default value.
+ allow_none : bool [ default False ]
+ Indicates whether None is allowed as a value.
**kwargs
Extra kwargs passed to `ClassBasedTraitType`
-
- Notes
- -----
- If both ``args`` and ``kw`` are None, then the default value is None.
- If ``args`` is a tuple and ``kw`` is a dict, then the default is
- created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is
- None, the None is replaced by ``()`` or ``{}``, respectively.
- """
- if klass is None:
- klass = self.klass
+
+ Notes
+ -----
+ If both ``args`` and ``kw`` are None, then the default value is None.
+ If ``args`` is a tuple and ``kw`` is a dict, then the default is
+ created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is
+ None, the None is replaced by ``()`` or ``{}``, respectively.
+ """
+ if klass is None:
+ klass = self.klass
if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, str)):
- self.klass = klass
- else:
- raise TraitError('The klass attribute must be a class'
- ' not: %r' % klass)
-
- if (kw is not None) and not isinstance(kw, dict):
- raise TraitError("The 'kw' argument must be a dict or None.")
- if (args is not None) and not isinstance(args, tuple):
- raise TraitError("The 'args' argument must be a tuple or None.")
-
- self.default_args = args
- self.default_kwargs = kw
-
+ self.klass = klass
+ else:
+ raise TraitError('The klass attribute must be a class'
+ ' not: %r' % klass)
+
+ if (kw is not None) and not isinstance(kw, dict):
+ raise TraitError("The 'kw' argument must be a dict or None.")
+ if (args is not None) and not isinstance(args, tuple):
+ raise TraitError("The 'args' argument must be a tuple or None.")
+
+ self.default_args = args
+ self.default_kwargs = kw
+
super(Instance, self).__init__(**kwargs)
-
- def validate(self, obj, value):
- if isinstance(value, self.klass):
- return value
- else:
- self.error(obj, value)
-
- def info(self):
+
+ def validate(self, obj, value):
+ if isinstance(value, self.klass):
+ return value
+ else:
+ self.error(obj, value)
+
+ def info(self):
if isinstance(self.klass, str):
result = add_article(self.klass)
- else:
+ else:
result = describe("a", self.klass)
- if self.allow_none:
+ if self.allow_none:
result += ' or None'
- return result
-
- def instance_init(self, obj):
- self._resolve_classes()
+ return result
+
+ def instance_init(self, obj):
+ self._resolve_classes()
super().instance_init(obj)
-
- def _resolve_classes(self):
+
+ def _resolve_classes(self):
if isinstance(self.klass, str):
- self.klass = self._resolve_string(self.klass)
-
- def make_dynamic_default(self):
- if (self.default_args is None) and (self.default_kwargs is None):
- return None
- return self.klass(*(self.default_args or ()),
- **(self.default_kwargs or {}))
-
- def default_value_repr(self):
- return repr(self.make_dynamic_default())
-
+ self.klass = self._resolve_string(self.klass)
+
+ def make_dynamic_default(self):
+ if (self.default_args is None) and (self.default_kwargs is None):
+ return None
+ return self.klass(*(self.default_args or ()),
+ **(self.default_kwargs or {}))
+
+ def default_value_repr(self):
+ return repr(self.make_dynamic_default())
+
def from_string(self, s):
return _safe_literal_eval(s)
-
-
-class ForwardDeclaredMixin(object):
- """
- Mixin for forward-declared versions of Instance and Type.
- """
- def _resolve_string(self, string):
- """
- Find the specified class name by looking for it in the module in which
- our this_class attribute was defined.
- """
- modname = self.this_class.__module__
- return import_item('.'.join([modname, string]))
-
-
-class ForwardDeclaredType(ForwardDeclaredMixin, Type):
- """
- Forward-declared version of Type.
- """
- pass
-
-
-class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
- """
- Forward-declared version of Instance.
- """
- pass
-
-
-class This(ClassBasedTraitType):
- """A trait for instances of the class containing this trait.
-
- Because how how and when class bodies are executed, the ``This``
- trait can only have a default value of None. This, and because we
- always validate default values, ``allow_none`` is *always* true.
- """
-
- info_text = 'an instance of the same type as the receiver or None'
-
+
+
+class ForwardDeclaredMixin(object):
+ """
+ Mixin for forward-declared versions of Instance and Type.
+ """
+ def _resolve_string(self, string):
+ """
+ Find the specified class name by looking for it in the module in which
+ our this_class attribute was defined.
+ """
+ modname = self.this_class.__module__
+ return import_item('.'.join([modname, string]))
+
+
+class ForwardDeclaredType(ForwardDeclaredMixin, Type):
+ """
+ Forward-declared version of Type.
+ """
+ pass
+
+
+class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
+ """
+ Forward-declared version of Instance.
+ """
+ pass
+
+
+class This(ClassBasedTraitType):
+ """A trait for instances of the class containing this trait.
+
+ Because how how and when class bodies are executed, the ``This``
+ trait can only have a default value of None. This, and because we
+ always validate default values, ``allow_none`` is *always* true.
+ """
+
+ info_text = 'an instance of the same type as the receiver or None'
+
def __init__(self, **kwargs):
super(This, self).__init__(None, **kwargs)
-
- def validate(self, obj, value):
- # What if value is a superclass of obj.__class__? This is
- # complicated if it was the superclass that defined the This
- # trait.
- if isinstance(value, self.this_class) or (value is None):
- return value
- else:
- self.error(obj, value)
-
-
-class Union(TraitType):
- """A trait type representing a Union type."""
-
+
+ def validate(self, obj, value):
+ # What if value is a superclass of obj.__class__? This is
+ # complicated if it was the superclass that defined the This
+ # trait.
+ if isinstance(value, self.this_class) or (value is None):
+ return value
+ else:
+ self.error(obj, value)
+
+
+class Union(TraitType):
+ """A trait type representing a Union type."""
+
def __init__(self, trait_types, **kwargs):
- """Construct a Union trait.
-
- This trait allows values that are allowed by at least one of the
- specified trait types. A Union traitlet cannot have metadata on
- its own, besides the metadata of the listed types.
-
- Parameters
- ----------
+ """Construct a Union trait.
+
+ This trait allows values that are allowed by at least one of the
+ specified trait types. A Union traitlet cannot have metadata on
+ its own, besides the metadata of the listed types.
+
+ Parameters
+ ----------
trait_types : sequence
- The list of trait types of length at least 1.
-
- Notes
- -----
- Union([Float(), Bool(), Int()]) attempts to validate the provided values
- with the validation function of Float, then Bool, and finally Int.
- """
+ The list of trait types of length at least 1.
+
+ Notes
+ -----
+ Union([Float(), Bool(), Int()]) attempts to validate the provided values
+ with the validation function of Float, then Bool, and finally Int.
+ """
self.trait_types = list(trait_types)
self.info_text = " or ".join([tt.info() for tt in self.trait_types])
super(Union, self).__init__(**kwargs)
-
+
def default(self, obj=None):
default = super(Union, self).default(obj)
for t in self.trait_types:
@@ -1963,48 +1963,48 @@ class Union(TraitType):
break
return default
- def class_init(self, cls, name):
+ def class_init(self, cls, name):
for trait_type in reversed(self.trait_types):
- trait_type.class_init(cls, None)
- super(Union, self).class_init(cls, name)
-
- def instance_init(self, obj):
+ trait_type.class_init(cls, None)
+ super(Union, self).class_init(cls, name)
+
+ def instance_init(self, obj):
for trait_type in reversed(self.trait_types):
- trait_type.instance_init(obj)
- super(Union, self).instance_init(obj)
-
- def validate(self, obj, value):
+ trait_type.instance_init(obj)
+ super(Union, self).instance_init(obj)
+
+ def validate(self, obj, value):
with obj.cross_validation_lock:
- for trait_type in self.trait_types:
- try:
- v = trait_type._validate(obj, value)
+ for trait_type in self.trait_types:
+ try:
+ v = trait_type._validate(obj, value)
# In the case of an element trait, the name is None
if self.name is not None:
setattr(obj, '_' + self.name + '_metadata', trait_type.metadata)
- return v
- except TraitError:
- continue
- self.error(obj, value)
-
- def __or__(self, other):
- if isinstance(other, Union):
- return Union(self.trait_types + other.trait_types)
- else:
- return Union(self.trait_types + [other])
-
-
-#-----------------------------------------------------------------------------
-# Basic TraitTypes implementations/subclasses
-#-----------------------------------------------------------------------------
-
-
-class Any(TraitType):
- """A trait which allows any value."""
- default_value = None
+ return v
+ except TraitError:
+ continue
+ self.error(obj, value)
+
+ def __or__(self, other):
+ if isinstance(other, Union):
+ return Union(self.trait_types + other.trait_types)
+ else:
+ return Union(self.trait_types + [other])
+
+
+#-----------------------------------------------------------------------------
+# Basic TraitTypes implementations/subclasses
+#-----------------------------------------------------------------------------
+
+
+class Any(TraitType):
+ """A trait which allows any value."""
+ default_value = None
allow_none = True
- info_text = 'any value'
-
-
+ info_text = 'any value'
+
+
def _validate_bounds(trait, obj, value):
"""
Validate that a number to be applied to a trait is between bounds.
@@ -2029,122 +2029,122 @@ def _validate_bounds(trait, obj, value):
return value
-class Int(TraitType):
- """An int trait."""
-
- default_value = 0
- info_text = 'an int'
-
+class Int(TraitType):
+ """An int trait."""
+
+ default_value = 0
+ info_text = 'an int'
+
def __init__(self, default_value=Undefined, allow_none=False, **kwargs):
- self.min = kwargs.pop('min', None)
- self.max = kwargs.pop('max', None)
- super(Int, self).__init__(default_value=default_value,
- allow_none=allow_none, **kwargs)
-
- def validate(self, obj, value):
- if not isinstance(value, int):
- self.error(obj, value)
+ self.min = kwargs.pop('min', None)
+ self.max = kwargs.pop('max', None)
+ super(Int, self).__init__(default_value=default_value,
+ allow_none=allow_none, **kwargs)
+
+ def validate(self, obj, value):
+ if not isinstance(value, int):
+ self.error(obj, value)
return _validate_bounds(self, obj, value)
-
+
def from_string(self, s):
if self.allow_none and s == 'None':
return None
return int(s)
+
-
-class CInt(Int):
- """A casting version of the int trait."""
-
- def validate(self, obj, value):
- try:
+class CInt(Int):
+ """A casting version of the int trait."""
+
+ def validate(self, obj, value):
+ try:
value = int(value)
except Exception:
- self.error(obj, value)
+ self.error(obj, value)
return _validate_bounds(self, obj, value)
-
+
Long, CLong = Int, CInt
Integer = Int
-
-
-class Float(TraitType):
- """A float trait."""
-
- default_value = 0.0
- info_text = 'a float'
-
+
+
+class Float(TraitType):
+ """A float trait."""
+
+ default_value = 0.0
+ info_text = 'a float'
+
def __init__(self, default_value=Undefined, allow_none=False, **kwargs):
- self.min = kwargs.pop('min', -float('inf'))
- self.max = kwargs.pop('max', float('inf'))
+ self.min = kwargs.pop('min', -float('inf'))
+ self.max = kwargs.pop('max', float('inf'))
super(Float, self).__init__(default_value=default_value,
- allow_none=allow_none, **kwargs)
-
- def validate(self, obj, value):
- if isinstance(value, int):
- value = float(value)
- if not isinstance(value, float):
- self.error(obj, value)
+ allow_none=allow_none, **kwargs)
+
+ def validate(self, obj, value):
+ if isinstance(value, int):
+ value = float(value)
+ if not isinstance(value, float):
+ self.error(obj, value)
return _validate_bounds(self, obj, value)
-
+
def from_string(self, s):
if self.allow_none and s == 'None':
return None
return float(s)
+
-
-class CFloat(Float):
- """A casting version of the float trait."""
-
- def validate(self, obj, value):
- try:
+class CFloat(Float):
+ """A casting version of the float trait."""
+
+ def validate(self, obj, value):
+ try:
value = float(value)
except Exception:
- self.error(obj, value)
+ self.error(obj, value)
return _validate_bounds(self, obj, value)
-
-
-class Complex(TraitType):
- """A trait for complex numbers."""
-
- default_value = 0.0 + 0.0j
- info_text = 'a complex number'
-
- def validate(self, obj, value):
- if isinstance(value, complex):
- return value
- if isinstance(value, (float, int)):
- return complex(value)
- self.error(obj, value)
-
+
+
+class Complex(TraitType):
+ """A trait for complex numbers."""
+
+ default_value = 0.0 + 0.0j
+ info_text = 'a complex number'
+
+ def validate(self, obj, value):
+ if isinstance(value, complex):
+ return value
+ if isinstance(value, (float, int)):
+ return complex(value)
+ self.error(obj, value)
+
def from_string(self, s):
if self.allow_none and s == 'None':
return None
return complex(s)
-
-
-class CComplex(Complex):
- """A casting version of the complex number trait."""
-
- def validate (self, obj, value):
- try:
- return complex(value)
+
+
+class CComplex(Complex):
+ """A casting version of the complex number trait."""
+
+ def validate (self, obj, value):
+ try:
+ return complex(value)
except Exception:
- self.error(obj, value)
-
-# We should always be explicit about whether we're using bytes or unicode, both
-# for Python 3 conversion and for reliable unicode behaviour on Python 2. So
-# we don't have a Str type.
-class Bytes(TraitType):
- """A trait for byte strings."""
-
- default_value = b''
- info_text = 'a bytes object'
-
- def validate(self, obj, value):
- if isinstance(value, bytes):
- return value
- self.error(obj, value)
-
+ self.error(obj, value)
+
+# We should always be explicit about whether we're using bytes or unicode, both
+# for Python 3 conversion and for reliable unicode behaviour on Python 2. So
+# we don't have a Str type.
+class Bytes(TraitType):
+ """A trait for byte strings."""
+
+ default_value = b''
+ info_text = 'a bytes object'
+
+ def validate(self, obj, value):
+ if isinstance(value, bytes):
+ return value
+ self.error(obj, value)
+
def from_string(self, s):
if self.allow_none and s == "None":
return None
@@ -2160,35 +2160,35 @@ class Bytes(TraitType):
FutureWarning)
break
return s.encode("utf8")
-
-
-class CBytes(Bytes):
- """A casting version of the byte string trait."""
-
- def validate(self, obj, value):
- try:
- return bytes(value)
+
+
+class CBytes(Bytes):
+ """A casting version of the byte string trait."""
+
+ def validate(self, obj, value):
+ try:
+ return bytes(value)
except Exception:
- self.error(obj, value)
-
-
-class Unicode(TraitType):
- """A trait for unicode strings."""
-
+ self.error(obj, value)
+
+
+class Unicode(TraitType):
+ """A trait for unicode strings."""
+
default_value = ''
- info_text = 'a unicode string'
-
- def validate(self, obj, value):
+ info_text = 'a unicode string'
+
+ def validate(self, obj, value):
if isinstance(value, str):
- return value
- if isinstance(value, bytes):
- try:
- return value.decode('ascii', 'strict')
- except UnicodeDecodeError:
- msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
- raise TraitError(msg.format(value, self.name, class_of(obj)))
- self.error(obj, value)
-
+ return value
+ if isinstance(value, bytes):
+ try:
+ return value.decode('ascii', 'strict')
+ except UnicodeDecodeError:
+ msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
+ raise TraitError(msg.format(value, self.name, class_of(obj)))
+ self.error(obj, value)
+
def from_string(self, s):
if self.allow_none and s == 'None':
return None
@@ -2204,66 +2204,66 @@ class Unicode(TraitType):
"You can use %r instead of %r if you require traitlets >=5." % (s, old_s),
FutureWarning)
return s
+
-
-class CUnicode(Unicode):
- """A casting version of the unicode trait."""
-
- def validate(self, obj, value):
- try:
+class CUnicode(Unicode):
+ """A casting version of the unicode trait."""
+
+ def validate(self, obj, value):
+ try:
return str(value)
except Exception:
- self.error(obj, value)
-
-
-class ObjectName(TraitType):
- """A string holding a valid object name in this version of Python.
-
- This does not check that the name exists in any scope."""
- info_text = "a valid object identifier in Python"
-
+ self.error(obj, value)
+
+
+class ObjectName(TraitType):
+ """A string holding a valid object name in this version of Python.
+
+ This does not check that the name exists in any scope."""
+ info_text = "a valid object identifier in Python"
+
coerce_str = staticmethod(lambda _,s: s)
-
- def validate(self, obj, value):
- value = self.coerce_str(obj, value)
-
+
+ def validate(self, obj, value):
+ value = self.coerce_str(obj, value)
+
if isinstance(value, str) and isidentifier(value):
- return value
- self.error(obj, value)
-
+ return value
+ self.error(obj, value)
+
def from_string(self, s):
if self.allow_none and s == 'None':
return None
return s
-class DottedObjectName(ObjectName):
- """A string holding a valid dotted object name in Python, such as A.b3._c"""
- def validate(self, obj, value):
- value = self.coerce_str(obj, value)
-
+class DottedObjectName(ObjectName):
+ """A string holding a valid dotted object name in Python, such as A.b3._c"""
+ def validate(self, obj, value):
+ value = self.coerce_str(obj, value)
+
if isinstance(value, str) and all(isidentifier(a)
for a in value.split('.')):
- return value
- self.error(obj, value)
-
-
-class Bool(TraitType):
- """A boolean (True, False) trait."""
-
- default_value = False
- info_text = 'a boolean'
-
- def validate(self, obj, value):
- if isinstance(value, bool):
- return value
+ return value
+ self.error(obj, value)
+
+
+class Bool(TraitType):
+ """A boolean (True, False) trait."""
+
+ default_value = False
+ info_text = 'a boolean'
+
+ def validate(self, obj, value):
+ if isinstance(value, bool):
+ return value
elif isinstance(value, int):
if value == 1:
return True
elif value == 0:
return False
- self.error(obj, value)
-
+ self.error(obj, value)
+
def from_string(self, s):
if self.allow_none and s == 'None':
return None
@@ -2274,32 +2274,32 @@ class Bool(TraitType):
return False
else:
raise ValueError("%r is not 1, 0, true, or false")
-
-
-class CBool(Bool):
- """A casting version of the boolean trait."""
-
- def validate(self, obj, value):
- try:
- return bool(value)
+
+
+class CBool(Bool):
+ """A casting version of the boolean trait."""
+
+ def validate(self, obj, value):
+ try:
+ return bool(value)
except Exception:
- self.error(obj, value)
-
-
-class Enum(TraitType):
- """An enum whose value must be in a given sequence."""
-
+ self.error(obj, value)
+
+
+class Enum(TraitType):
+ """An enum whose value must be in a given sequence."""
+
def __init__(self, values, default_value=Undefined, **kwargs):
- self.values = values
+ self.values = values
if kwargs.get('allow_none', False) and default_value is Undefined:
- default_value = None
+ default_value = None
super(Enum, self).__init__(default_value, **kwargs)
-
- def validate(self, obj, value):
- if value in self.values:
- return value
- self.error(obj, value)
-
+
+ def validate(self, obj, value):
+ if value in self.values:
+ return value
+ self.error(obj, value)
+
def _choices_str(self, as_rst=False):
""" Returns a description of the trait choices (not none)."""
choices = self.values
@@ -2310,12 +2310,12 @@ class Enum(TraitType):
return choices
def _info(self, as_rst=False):
- """ Returns a description of the trait."""
+ """ Returns a description of the trait."""
none = (' or %s' % ('`None`' if as_rst else 'None')
if self.allow_none else
'')
return 'any of %s%s' % (self._choices_str(as_rst), none)
-
+
def info(self):
return self._info(as_rst=False)
@@ -2329,21 +2329,21 @@ class Enum(TraitType):
return _safe_literal_eval(s)
-class CaselessStrEnum(Enum):
- """An enum of strings where the case should be ignored."""
+class CaselessStrEnum(Enum):
+ """An enum of strings where the case should be ignored."""
def __init__(self, values, default_value=Undefined, **kwargs):
super().__init__(values, default_value=default_value, **kwargs)
- def validate(self, obj, value):
+ def validate(self, obj, value):
if not isinstance(value, str):
- self.error(obj, value)
-
- for v in self.values:
- if v.lower() == value.lower():
- return v
- self.error(obj, value)
-
+ self.error(obj, value)
+
+ for v in self.values:
+ if v.lower() == value.lower():
+ return v
+ self.error(obj, value)
+
def _info(self, as_rst=False):
""" Returns a description of the trait."""
none = (' or %s' % ('`None`' if as_rst else 'None')
@@ -2408,52 +2408,52 @@ class FuzzyEnum(Enum):
return self._info(as_rst=True)
-class Container(Instance):
- """An instance of a container (list, set, etc.)
-
- To be subclassed by overriding klass.
- """
+class Container(Instance):
+ """An instance of a container (list, set, etc.)
+
+ To be subclassed by overriding klass.
+ """
- klass = None
- _cast_types = ()
- _valid_defaults = SequenceTypes
- _trait = None
+ klass = None
+ _cast_types = ()
+ _valid_defaults = SequenceTypes
+ _trait = None
_literal_from_string_pairs = ("[]", "()")
-
+
def __init__(self, trait=None, default_value=Undefined, **kwargs):
- """Create a container trait type from a list, set, or tuple.
-
- The default value is created by doing ``List(default_value)``,
- which creates a copy of the ``default_value``.
-
- ``trait`` can be specified, which restricts the type of elements
- in the container to that TraitType.
-
- If only one arg is given and it is not a Trait, it is taken as
- ``default_value``:
-
- ``c = List([1, 2, 3])``
-
- Parameters
- ----------
- trait : TraitType [ optional ]
- the type for restricting the contents of the Container. If unspecified,
- types are not checked.
- default_value : SequenceType [ optional ]
- The default value for the Trait. Must be list/tuple/set, and
- will be cast to the container type.
- allow_none : bool [ default False ]
- Whether to allow the value to be None
+ """Create a container trait type from a list, set, or tuple.
+
+ The default value is created by doing ``List(default_value)``,
+ which creates a copy of the ``default_value``.
+
+ ``trait`` can be specified, which restricts the type of elements
+ in the container to that TraitType.
+
+ If only one arg is given and it is not a Trait, it is taken as
+ ``default_value``:
+
+ ``c = List([1, 2, 3])``
+
+ Parameters
+ ----------
+ trait : TraitType [ optional ]
+ the type for restricting the contents of the Container. If unspecified,
+ types are not checked.
+ default_value : SequenceType [ optional ]
+ The default value for the Trait. Must be list/tuple/set, and
+ will be cast to the container type.
+ allow_none : bool [ default False ]
+ Whether to allow the value to be None
**kwargs : any
- further keys for extensions to the Trait (e.g. config)
+ further keys for extensions to the Trait (e.g. config)
+
+ """
- """
-
- # allow List([values]):
+ # allow List([values]):
if trait is not None and default_value is Undefined and not is_trait(trait):
- default_value = trait
- trait = None
-
+ default_value = trait
+ trait = None
+
if default_value is None and not kwargs.get("allow_none", False):
# improve backward-compatibility for possible subclasses
# specifying default_value=None as default,
@@ -2468,68 +2468,68 @@ class Container(Instance):
default_value = Undefined
if default_value is Undefined:
- args = ()
+ args = ()
elif default_value is None:
# default_value back on kwargs for super() to handle
args = ()
kwargs["default_value"] = None
- elif isinstance(default_value, self._valid_defaults):
- args = (default_value,)
- else:
+ elif isinstance(default_value, self._valid_defaults):
+ args = (default_value,)
+ else:
raise TypeError(
"default value of %s was %s" % (self.__class__.__name__, default_value)
)
-
- if is_trait(trait):
- if isinstance(trait, type):
+
+ if is_trait(trait):
+ if isinstance(trait, type):
warn(
"Traits should be given as instances, not types (for example, `Int()`, not `Int`)."
" Passing types is deprecated in traitlets 4.1.",
DeprecationWarning,
stacklevel=3,
)
- self._trait = trait() if isinstance(trait, type) else trait
- elif trait is not None:
+ self._trait = trait() if isinstance(trait, type) else trait
+ elif trait is not None:
raise TypeError(
"`trait` must be a Trait or None, got %s" % repr_type(trait)
)
-
+
super(Container, self).__init__(klass=self.klass, args=args, **kwargs)
-
- def validate(self, obj, value):
- if isinstance(value, self._cast_types):
- value = self.klass(value)
- value = super(Container, self).validate(obj, value)
- if value is None:
- return value
-
- value = self.validate_elements(obj, value)
-
- return value
-
- def validate_elements(self, obj, value):
- validated = []
- if self._trait is None or isinstance(self._trait, Any):
- return value
- for v in value:
- try:
- v = self._trait._validate(obj, v)
+
+ def validate(self, obj, value):
+ if isinstance(value, self._cast_types):
+ value = self.klass(value)
+ value = super(Container, self).validate(obj, value)
+ if value is None:
+ return value
+
+ value = self.validate_elements(obj, value)
+
+ return value
+
+ def validate_elements(self, obj, value):
+ validated = []
+ if self._trait is None or isinstance(self._trait, Any):
+ return value
+ for v in value:
+ try:
+ v = self._trait._validate(obj, v)
except TraitError as error:
self.error(obj, v, error)
- else:
- validated.append(v)
- return self.klass(validated)
-
- def class_init(self, cls, name):
- if isinstance(self._trait, TraitType):
- self._trait.class_init(cls, None)
- super(Container, self).class_init(cls, name)
-
- def instance_init(self, obj):
- if isinstance(self._trait, TraitType):
- self._trait.instance_init(obj)
- super(Container, self).instance_init(obj)
-
+ else:
+ validated.append(v)
+ return self.klass(validated)
+
+ def class_init(self, cls, name):
+ if isinstance(self._trait, TraitType):
+ self._trait.class_init(cls, None)
+ super(Container, self).class_init(cls, name)
+
+ def instance_init(self, obj):
+ if isinstance(self._trait, TraitType):
+ self._trait.instance_init(obj)
+ super(Container, self).instance_init(obj)
+
def from_string(self, s):
"""Load value from a single string"""
if not isinstance(s, str):
@@ -2539,7 +2539,7 @@ class Container(Instance):
except Exception:
test = None
return self.validate(None, test)
-
+
def from_string_list(self, s_list):
"""Return the value from a list of config strings
@@ -2588,11 +2588,11 @@ class Container(Instance):
return s
-class List(Container):
- """An instance of a Python list."""
- klass = list
- _cast_types = (tuple,)
-
+class List(Container):
+ """An instance of a Python list."""
+ klass = list
+ _cast_types = (tuple,)
+
def __init__(
self,
trait=None,
@@ -2601,64 +2601,64 @@ class List(Container):
maxlen=sys.maxsize,
**kwargs,
):
- """Create a List trait type from a list, set, or tuple.
-
- The default value is created by doing ``list(default_value)``,
- which creates a copy of the ``default_value``.
-
- ``trait`` can be specified, which restricts the type of elements
- in the container to that TraitType.
-
- If only one arg is given and it is not a Trait, it is taken as
- ``default_value``:
-
- ``c = List([1, 2, 3])``
-
- Parameters
- ----------
- trait : TraitType [ optional ]
- the type for restricting the contents of the Container.
- If unspecified, types are not checked.
- default_value : SequenceType [ optional ]
- The default value for the Trait. Must be list/tuple/set, and
- will be cast to the container type.
- minlen : Int [ default 0 ]
- The minimum length of the input list
- maxlen : Int [ default sys.maxsize ]
- The maximum length of the input list
- """
- self._minlen = minlen
- self._maxlen = maxlen
- super(List, self).__init__(trait=trait, default_value=default_value,
+ """Create a List trait type from a list, set, or tuple.
+
+ The default value is created by doing ``list(default_value)``,
+ which creates a copy of the ``default_value``.
+
+ ``trait`` can be specified, which restricts the type of elements
+ in the container to that TraitType.
+
+ If only one arg is given and it is not a Trait, it is taken as
+ ``default_value``:
+
+ ``c = List([1, 2, 3])``
+
+ Parameters
+ ----------
+ trait : TraitType [ optional ]
+ the type for restricting the contents of the Container.
+ If unspecified, types are not checked.
+ default_value : SequenceType [ optional ]
+ The default value for the Trait. Must be list/tuple/set, and
+ will be cast to the container type.
+ minlen : Int [ default 0 ]
+ The minimum length of the input list
+ maxlen : Int [ default sys.maxsize ]
+ The maximum length of the input list
+ """
+ self._minlen = minlen
+ self._maxlen = maxlen
+ super(List, self).__init__(trait=trait, default_value=default_value,
**kwargs)
-
- def length_error(self, obj, value):
- e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
- % (self.name, class_of(obj), self._minlen, self._maxlen, value)
- raise TraitError(e)
-
- def validate_elements(self, obj, value):
- length = len(value)
- if length < self._minlen or length > self._maxlen:
- self.length_error(obj, value)
-
- return super(List, self).validate_elements(obj, value)
-
+
+ def length_error(self, obj, value):
+ e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
+ % (self.name, class_of(obj), self._minlen, self._maxlen, value)
+ raise TraitError(e)
+
+ def validate_elements(self, obj, value):
+ length = len(value)
+ if length < self._minlen or length > self._maxlen:
+ self.length_error(obj, value)
+
+ return super(List, self).validate_elements(obj, value)
+
def set(self, obj, value):
if isinstance(value, str):
return super().set(obj, [value])
else:
return super().set(obj, value)
+
-
-class Set(List):
- """An instance of a Python set."""
- klass = set
- _cast_types = (tuple, list)
-
+class Set(List):
+ """An instance of a Python set."""
+ klass = set
+ _cast_types = (tuple, list)
+
_literal_from_string_pairs = ("[]", "()", "{}")
- # Redefine __init__ just to make the docstring more accurate.
+ # Redefine __init__ just to make the docstring more accurate.
def __init__(
self,
trait=None,
@@ -2667,84 +2667,84 @@ class Set(List):
maxlen=sys.maxsize,
**kwargs,
):
- """Create a Set trait type from a list, set, or tuple.
-
- The default value is created by doing ``set(default_value)``,
- which creates a copy of the ``default_value``.
-
- ``trait`` can be specified, which restricts the type of elements
- in the container to that TraitType.
-
- If only one arg is given and it is not a Trait, it is taken as
- ``default_value``:
-
- ``c = Set({1, 2, 3})``
-
- Parameters
- ----------
- trait : TraitType [ optional ]
- the type for restricting the contents of the Container.
- If unspecified, types are not checked.
- default_value : SequenceType [ optional ]
- The default value for the Trait. Must be list/tuple/set, and
- will be cast to the container type.
- minlen : Int [ default 0 ]
- The minimum length of the input list
- maxlen : Int [ default sys.maxsize ]
- The maximum length of the input list
- """
+ """Create a Set trait type from a list, set, or tuple.
+
+ The default value is created by doing ``set(default_value)``,
+ which creates a copy of the ``default_value``.
+
+ ``trait`` can be specified, which restricts the type of elements
+ in the container to that TraitType.
+
+ If only one arg is given and it is not a Trait, it is taken as
+ ``default_value``:
+
+ ``c = Set({1, 2, 3})``
+
+ Parameters
+ ----------
+ trait : TraitType [ optional ]
+ the type for restricting the contents of the Container.
+ If unspecified, types are not checked.
+ default_value : SequenceType [ optional ]
+ The default value for the Trait. Must be list/tuple/set, and
+ will be cast to the container type.
+ minlen : Int [ default 0 ]
+ The minimum length of the input list
+ maxlen : Int [ default sys.maxsize ]
+ The maximum length of the input list
+ """
super(Set, self).__init__(trait, default_value, minlen, maxlen, **kwargs)
-
+
def default_value_repr(self):
# Ensure default value is sorted for a reproducible build
list_repr = repr(sorted(self.make_dynamic_default()))
if list_repr == '[]':
return 'set()'
return '{'+list_repr[1:-1]+'}'
+
+class Tuple(Container):
+ """An instance of a Python tuple."""
-class Tuple(Container):
- """An instance of a Python tuple."""
-
- klass = tuple
- _cast_types = (list,)
-
+ klass = tuple
+ _cast_types = (list,)
+
def __init__(self, *traits, **kwargs):
- """Create a tuple from a list, set, or tuple.
-
- Create a fixed-type tuple with Traits:
-
- ``t = Tuple(Int(), Str(), CStr())``
-
- would be length 3, with Int,Str,CStr for each element.
-
- If only one arg is given and it is not a Trait, it is taken as
- default_value:
-
- ``t = Tuple((1, 2, 3))``
-
- Otherwise, ``default_value`` *must* be specified by keyword.
-
- Parameters
- ----------
+ """Create a tuple from a list, set, or tuple.
+
+ Create a fixed-type tuple with Traits:
+
+ ``t = Tuple(Int(), Str(), CStr())``
+
+ would be length 3, with Int,Str,CStr for each element.
+
+ If only one arg is given and it is not a Trait, it is taken as
+ default_value:
+
+ ``t = Tuple((1, 2, 3))``
+
+ Otherwise, ``default_value`` *must* be specified by keyword.
+
+ Parameters
+ ----------
*traits : TraitTypes [ optional ]
- the types for restricting the contents of the Tuple. If unspecified,
- types are not checked. If specified, then each positional argument
- corresponds to an element of the tuple. Tuples defined with traits
- are of fixed length.
- default_value : SequenceType [ optional ]
- The default value for the Tuple. Must be list/tuple/set, and
- will be cast to a tuple. If ``traits`` are specified,
- ``default_value`` must conform to the shape and type they specify.
+ the types for restricting the contents of the Tuple. If unspecified,
+ types are not checked. If specified, then each positional argument
+ corresponds to an element of the tuple. Tuples defined with traits
+ are of fixed length.
+ default_value : SequenceType [ optional ]
+ The default value for the Tuple. Must be list/tuple/set, and
+ will be cast to a tuple. If ``traits`` are specified,
+ ``default_value`` must conform to the shape and type they specify.
**kwargs
Other kwargs passed to `Container`
- """
+ """
default_value = kwargs.pop("default_value", Undefined)
- # allow Tuple((values,)):
- if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]):
- default_value = traits[0]
- traits = ()
-
+ # allow Tuple((values,)):
+ if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]):
+ default_value = traits[0]
+ traits = ()
+
if default_value is None and not kwargs.get("allow_none", False):
# improve backward-compatibility for possible subclasses
# specifying default_value=None as default,
@@ -2758,22 +2758,22 @@ class Tuple(Container):
)
default_value = Undefined
- if default_value is Undefined:
- args = ()
+ if default_value is Undefined:
+ args = ()
elif default_value is None:
# default_value back on kwargs for super() to handle
args = ()
kwargs["default_value"] = None
- elif isinstance(default_value, self._valid_defaults):
- args = (default_value,)
- else:
+ elif isinstance(default_value, self._valid_defaults):
+ args = (default_value,)
+ else:
raise TypeError(
"default value of %s was %s" % (self.__class__.__name__, default_value)
)
-
- self._traits = []
- for trait in traits:
- if isinstance(trait, type):
+
+ self._traits = []
+ for trait in traits:
+ if isinstance(trait, type):
warn(
"Traits should be given as instances, not types (for example, `Int()`, not `Int`)"
" Passing types is deprecated in traitlets 4.1.",
@@ -2782,12 +2782,12 @@ class Tuple(Container):
)
trait = trait()
self._traits.append(trait)
-
+
if self._traits and (default_value is None or default_value is Undefined):
- # don't allow default to be an empty container if length is specified
- args = None
+ # don't allow default to be an empty container if length is specified
+ args = None
super(Container, self).__init__(klass=self.klass, args=args, **kwargs)
-
+
def item_from_string(self, s, index):
"""Cast a single item from a string
@@ -2799,41 +2799,41 @@ class Tuple(Container):
return s
return self._traits[index].from_string(s)
- def validate_elements(self, obj, value):
- if not self._traits:
- # nothing to validate
- return value
- if len(value) != len(self._traits):
- e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
- % (self.name, class_of(obj), len(self._traits), repr_type(value))
- raise TraitError(e)
-
- validated = []
- for t, v in zip(self._traits, value):
- try:
- v = t._validate(obj, v)
+ def validate_elements(self, obj, value):
+ if not self._traits:
+ # nothing to validate
+ return value
+ if len(value) != len(self._traits):
+ e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
+ % (self.name, class_of(obj), len(self._traits), repr_type(value))
+ raise TraitError(e)
+
+ validated = []
+ for t, v in zip(self._traits, value):
+ try:
+ v = t._validate(obj, v)
except TraitError as error:
self.error(obj, v, error)
- else:
- validated.append(v)
- return tuple(validated)
-
- def class_init(self, cls, name):
- for trait in self._traits:
- if isinstance(trait, TraitType):
- trait.class_init(cls, None)
- super(Container, self).class_init(cls, name)
-
- def instance_init(self, obj):
- for trait in self._traits:
- if isinstance(trait, TraitType):
- trait.instance_init(obj)
- super(Container, self).instance_init(obj)
-
-
-class Dict(Instance):
+ else:
+ validated.append(v)
+ return tuple(validated)
+
+ def class_init(self, cls, name):
+ for trait in self._traits:
+ if isinstance(trait, TraitType):
+ trait.class_init(cls, None)
+ super(Container, self).class_init(cls, name)
+
+ def instance_init(self, obj):
+ for trait in self._traits:
+ if isinstance(trait, TraitType):
+ trait.instance_init(obj)
+ super(Container, self).instance_init(obj)
+
+
+class Dict(Instance):
"""An instance of a Python dict.
-
+
One or more traits can be passed to the constructor
to validate the keys and/or values of the dict.
If you need more detailed validation,
@@ -2851,10 +2851,10 @@ class Dict(Instance):
def __init__(self, value_trait=None, per_key_traits=None, key_trait=None, default_value=Undefined,
**kwargs):
"""Create a dict trait type from a Python dict.
-
- The default value is created by doing ``dict(default_value)``,
- which creates a copy of the ``default_value``.
-
+
+ The default value is created by doing ``dict(default_value)``,
+ which creates a copy of the ``default_value``.
+
Parameters
----------
value_trait : TraitType [ optional ]
@@ -2876,15 +2876,15 @@ class Dict(Instance):
--------
>>> d = Dict(Unicode())
a dict whose values must be text
-
+
>>> d2 = Dict(per_key_traits={"n": Integer(), "s": Unicode()})
d2['n'] must be an integer
d2['s'] must be text
-
+
>>> d3 = Dict(value_trait=Integer(), key_trait=Unicode())
d3's keys must be text
d3's values must be integers
- """
+ """
# handle deprecated keywords
trait = kwargs.pop('trait', None)
@@ -2908,40 +2908,40 @@ class Dict(Instance):
stacklevel=2,
)
- # Handling positional arguments
+ # Handling positional arguments
if default_value is Undefined and value_trait is not None:
if not is_trait(value_trait):
default_value = value_trait
value_trait = None
-
+
if key_trait is None and per_key_traits is not None:
if is_trait(per_key_traits):
key_trait = per_key_traits
per_key_traits = None
- # Handling default value
- if default_value is Undefined:
- default_value = {}
- if default_value is None:
- args = None
- elif isinstance(default_value, dict):
- args = (default_value,)
- elif isinstance(default_value, SequenceTypes):
- args = (default_value,)
- else:
- raise TypeError('default value of Dict was %s' % default_value)
-
- # Case where a type of TraitType is provided rather than an instance
+ # Handling default value
+ if default_value is Undefined:
+ default_value = {}
+ if default_value is None:
+ args = None
+ elif isinstance(default_value, dict):
+ args = (default_value,)
+ elif isinstance(default_value, SequenceTypes):
+ args = (default_value,)
+ else:
+ raise TypeError('default value of Dict was %s' % default_value)
+
+ # Case where a type of TraitType is provided rather than an instance
if is_trait(value_trait):
if isinstance(value_trait, type):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)"
" Passing types is deprecated in traitlets 4.1.",
- DeprecationWarning, stacklevel=2)
+ DeprecationWarning, stacklevel=2)
value_trait = value_trait()
self._value_trait = value_trait
elif value_trait is not None:
raise TypeError("`value_trait` must be a Trait or None, got %s" % repr_type(value_trait))
-
+
if is_trait(key_trait):
if isinstance(key_trait, type):
warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)"
@@ -2951,32 +2951,32 @@ class Dict(Instance):
self._key_trait = key_trait
elif key_trait is not None:
raise TypeError("`key_trait` must be a Trait or None, got %s" % repr_type(key_trait))
-
+
self._per_key_traits = per_key_traits
super(Dict, self).__init__(klass=dict, args=args, **kwargs)
-
+
def element_error(self, obj, element, validator, side='Values'):
e = side + " of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
- % (self.name, class_of(obj), validator.info(), repr_type(element))
- raise TraitError(e)
-
- def validate(self, obj, value):
- value = super(Dict, self).validate(obj, value)
- if value is None:
- return value
- value = self.validate_elements(obj, value)
- return value
-
- def validate_elements(self, obj, value):
+ % (self.name, class_of(obj), validator.info(), repr_type(element))
+ raise TraitError(e)
+
+ def validate(self, obj, value):
+ value = super(Dict, self).validate(obj, value)
+ if value is None:
+ return value
+ value = self.validate_elements(obj, value)
+ return value
+
+ def validate_elements(self, obj, value):
per_key_override = self._per_key_traits or {}
key_trait = self._key_trait
value_trait = self._value_trait
if not (key_trait or value_trait or per_key_override):
- return value
+ return value
- validated = {}
- for key in value:
+ validated = {}
+ for key in value:
v = value[key]
if key_trait:
try:
@@ -2991,28 +2991,28 @@ class Dict(Instance):
self.element_error(obj, v, active_value_trait, 'Values')
validated[key] = v
- return self.klass(validated)
-
- def class_init(self, cls, name):
+ return self.klass(validated)
+
+ def class_init(self, cls, name):
if isinstance(self._value_trait, TraitType):
self._value_trait.class_init(cls, None)
if isinstance(self._key_trait, TraitType):
self._key_trait.class_init(cls, None)
if self._per_key_traits is not None:
for trait in self._per_key_traits.values():
- trait.class_init(cls, None)
- super(Dict, self).class_init(cls, name)
-
- def instance_init(self, obj):
+ trait.class_init(cls, None)
+ super(Dict, self).class_init(cls, name)
+
+ def instance_init(self, obj):
if isinstance(self._value_trait, TraitType):
self._value_trait.instance_init(obj)
if isinstance(self._key_trait, TraitType):
self._key_trait.instance_init(obj)
if self._per_key_traits is not None:
for trait in self._per_key_traits.values():
- trait.instance_init(obj)
- super(Dict, self).instance_init(obj)
-
+ trait.instance_init(obj)
+ super(Dict, self).instance_init(obj)
+
def from_string(self, s):
"""Load value from a single string"""
if not isinstance(s, str):
@@ -3024,7 +3024,7 @@ class Dict(Instance):
if isinstance(test, dict):
return test
raise
-
+
def from_string_list(self, s_list):
"""Return a dict from a list of config strings.
@@ -3086,24 +3086,24 @@ class Dict(Instance):
return {key: value}
-class TCPAddress(TraitType):
- """A trait for an (ip, port) tuple.
-
- This allows for both IPv4 IP addresses as well as hostnames.
- """
-
- default_value = ('127.0.0.1', 0)
- info_text = 'an (ip, port) tuple'
-
- def validate(self, obj, value):
- if isinstance(value, tuple):
- if len(value) == 2:
+class TCPAddress(TraitType):
+ """A trait for an (ip, port) tuple.
+
+ This allows for both IPv4 IP addresses as well as hostnames.
+ """
+
+ default_value = ('127.0.0.1', 0)
+ info_text = 'an (ip, port) tuple'
+
+ def validate(self, obj, value):
+ if isinstance(value, tuple):
+ if len(value) == 2:
if isinstance(value[0], str) and isinstance(value[1], int):
- port = value[1]
- if port >= 0 and port <= 65535:
- return value
- self.error(obj, value)
-
+ port = value[1]
+ if port >= 0 and port <= 65535:
+ return value
+ self.error(obj, value)
+
def from_string(self, s):
if self.allow_none and s == 'None':
return None
@@ -3114,19 +3114,19 @@ class TCPAddress(TraitType):
return (ip, port)
-class CRegExp(TraitType):
- """A casting compiled regular expression trait.
-
- Accepts both strings and compiled regular expressions. The resulting
- attribute will be a compiled regular expression."""
-
- info_text = 'a regular expression'
-
- def validate(self, obj, value):
- try:
- return re.compile(value)
+class CRegExp(TraitType):
+ """A casting compiled regular expression trait.
+
+ Accepts both strings and compiled regular expressions. The resulting
+ attribute will be a compiled regular expression."""
+
+ info_text = 'a regular expression'
+
+ def validate(self, obj, value):
+ try:
+ return re.compile(value)
except Exception:
- self.error(obj, value)
+ self.error(obj, value)
class UseEnum(TraitType):
diff --git a/contrib/python/traitlets/py3/traitlets/utils/getargspec.py b/contrib/python/traitlets/py3/traitlets/utils/getargspec.py
index 22511437bd..086b21999a 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/getargspec.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/getargspec.py
@@ -1,19 +1,19 @@
-"""
- getargspec excerpted from:
-
- sphinx.util.inspect
- ~~~~~~~~~~~~~~~~~~~
- Helpers for inspecting Python modules.
- :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-import inspect
-
-# Unmodified from sphinx below this line
-
+"""
+ getargspec excerpted from:
+
+ sphinx.util.inspect
+ ~~~~~~~~~~~~~~~~~~~
+ Helpers for inspecting Python modules.
+ :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import inspect
+
+# Unmodified from sphinx below this line
+
from functools import partial
-
+
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
@@ -29,12 +29,12 @@ def getargspec(func):
args = args[len(func.args):]
for arg in func.keywords or ():
try:
- i = args.index(arg) - len(args)
- del args[i]
- try:
+ i = args.index(arg) - len(args)
+ del args[i]
+ try:
del defaults[i]
- except IndexError:
- pass
+ except IndexError:
+ pass
except ValueError: # must be a kwonly arg
i = kwoargs.index(arg)
del kwoargs[i]
diff --git a/contrib/python/traitlets/py3/traitlets/utils/importstring.py b/contrib/python/traitlets/py3/traitlets/utils/importstring.py
index defad8f183..6d9e0986cd 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/importstring.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/importstring.py
@@ -1,38 +1,38 @@
-"""
-A simple utility to import something by its string name.
-"""
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-
-def import_item(name):
- """Import and return ``bar`` given the string ``foo.bar``.
-
- Calling ``bar = import_item("foo.bar")`` is the functional equivalent of
- executing the code ``from foo import bar``.
-
- Parameters
- ----------
- name : string
+"""
+A simple utility to import something by its string name.
+"""
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+
+def import_item(name):
+ """Import and return ``bar`` given the string ``foo.bar``.
+
+ Calling ``bar = import_item("foo.bar")`` is the functional equivalent of
+ executing the code ``from foo import bar``.
+
+ Parameters
+ ----------
+ name : string
The fully qualified name of the module/package being imported.
-
- Returns
- -------
- mod : module object
+
+ Returns
+ -------
+ mod : module object
The module that was imported.
- """
+ """
if not isinstance(name, str):
- raise TypeError("import_item accepts strings, not '%s'." % type(name))
- parts = name.rsplit('.', 1)
- if len(parts) == 2:
- # called with 'foo.bar....'
- package, obj = parts
- module = __import__(package, fromlist=[obj])
- try:
- pak = getattr(module, obj)
- except AttributeError:
- raise ImportError('No module named %s' % obj)
- return pak
- else:
- # called with un-dotted string
- return __import__(parts[0])
+ raise TypeError("import_item accepts strings, not '%s'." % type(name))
+ parts = name.rsplit('.', 1)
+ if len(parts) == 2:
+ # called with 'foo.bar....'
+ package, obj = parts
+ module = __import__(package, fromlist=[obj])
+ try:
+ pak = getattr(module, obj)
+ except AttributeError:
+ raise ImportError('No module named %s' % obj)
+ return pak
+ else:
+ # called with un-dotted string
+ return __import__(parts[0])
diff --git a/contrib/python/traitlets/py3/traitlets/utils/sentinel.py b/contrib/python/traitlets/py3/traitlets/utils/sentinel.py
index 0760bec8b5..de6b3e508f 100644
--- a/contrib/python/traitlets/py3/traitlets/utils/sentinel.py
+++ b/contrib/python/traitlets/py3/traitlets/utils/sentinel.py
@@ -1,20 +1,20 @@
-"""Sentinel class for constants with useful reprs"""
+"""Sentinel class for constants with useful reprs"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
-# Copyright (c) IPython Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-
-class Sentinel(object):
-
- def __init__(self, name, module, docstring=None):
- self.name = name
- self.module = module
- if docstring:
- self.__doc__ = docstring
-
- def __repr__(self):
+class Sentinel(object):
+
+ def __init__(self, name, module, docstring=None):
+ self.name = name
+ self.module = module
+ if docstring:
+ self.__doc__ = docstring
+
+ def __repr__(self):
return str(self.module) + '.' + self.name
-
+
def __copy__(self):
return self
diff --git a/contrib/python/traitlets/py3/ya.make b/contrib/python/traitlets/py3/ya.make
index 46980f21b3..be00a1bd39 100644
--- a/contrib/python/traitlets/py3/ya.make
+++ b/contrib/python/traitlets/py3/ya.make
@@ -1,49 +1,49 @@
# Generated by devtools/yamaker (pypi).
PY3_LIBRARY()
-
+
PROVIDES(python_traitlets)
OWNER(borman nslus g:python-contrib)
-
+
VERSION(5.1.1)
LICENSE(BSD-3-Clause)
NO_LINT()
-
-PY_SRCS(
- TOP_LEVEL
- traitlets/__init__.py
- traitlets/_version.py
- traitlets/config/__init__.py
- traitlets/config/application.py
- traitlets/config/configurable.py
- traitlets/config/loader.py
- traitlets/config/manager.py
+
+PY_SRCS(
+ TOP_LEVEL
+ traitlets/__init__.py
+ traitlets/_version.py
+ traitlets/config/__init__.py
+ traitlets/config/application.py
+ traitlets/config/configurable.py
+ traitlets/config/loader.py
+ traitlets/config/manager.py
traitlets/config/sphinxdoc.py
- traitlets/log.py
+ traitlets/log.py
traitlets/tests/__init__.py
traitlets/tests/_warnings.py
traitlets/tests/utils.py
- traitlets/traitlets.py
- traitlets/utils/__init__.py
+ traitlets/traitlets.py
+ traitlets/utils/__init__.py
traitlets/utils/bunch.py
traitlets/utils/decorators.py
traitlets/utils/descriptions.py
- traitlets/utils/getargspec.py
- traitlets/utils/importstring.py
- traitlets/utils/sentinel.py
+ traitlets/utils/getargspec.py
+ traitlets/utils/importstring.py
+ traitlets/utils/sentinel.py
traitlets/utils/text.py
-)
-
+)
+
RESOURCE_FILES(
PREFIX contrib/python/traitlets/py3/
.dist-info/METADATA
.dist-info/top_level.txt
)
-END()
+END()
RECURSE_FOR_TESTS(
tests
diff --git a/contrib/python/traitlets/ya.make b/contrib/python/traitlets/ya.make
index 3156aae8c5..934eb39823 100644
--- a/contrib/python/traitlets/ya.make
+++ b/contrib/python/traitlets/ya.make
@@ -1,5 +1,5 @@
PY23_LIBRARY()
-
+
LICENSE(Service-Py23-Proxy)
OWNER(g:python-contrib)
@@ -12,7 +12,7 @@ ENDIF()
NO_LINT()
-END()
+END()
RECURSE(
py2