diff options
| author | monster <[email protected]> | 2022-07-07 14:41:37 +0300 |
|---|---|---|
| committer | monster <[email protected]> | 2022-07-07 14:41:37 +0300 |
| commit | 06e5c21a835c0e923506c4ff27929f34e00761c2 (patch) | |
| tree | 75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /contrib/python/traitlets/py2 | |
| parent | 03f024c4412e3aa613bb543cf1660176320ba8f4 (diff) | |
fix ya.make
Diffstat (limited to 'contrib/python/traitlets/py2')
30 files changed, 0 insertions, 9351 deletions
diff --git a/contrib/python/traitlets/py2/.dist-info/METADATA b/contrib/python/traitlets/py2/.dist-info/METADATA deleted file mode 100644 index bd031a5c309..00000000000 --- a/contrib/python/traitlets/py2/.dist-info/METADATA +++ /dev/null @@ -1,34 +0,0 @@ -Metadata-Version: 2.1 -Name: traitlets -Version: 4.3.3 -Summary: Traitlets Python config system -Home-page: http://ipython.org -Author: IPython Development Team -Author-email: [email protected] -License: BSD -Keywords: Interactive,Interpreter,Shell,Web -Platform: Linux -Platform: Mac OS X -Platform: Windows -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: System Administrators -Classifier: Intended Audience :: Science/Research -Classifier: License :: OSI Approved :: BSD License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Requires-Dist: ipython-genutils -Requires-Dist: six -Requires-Dist: decorator -Requires-Dist: enum34 ; python_version=="2.7" -Requires-Dist: enum34 ; python_version=="3.3" -Provides-Extra: test -Requires-Dist: pytest ; extra == 'test' -Requires-Dist: mock ; (python_version=="2.7") and extra == 'test' - -A configuration system for Python applications. - - diff --git a/contrib/python/traitlets/py2/.dist-info/top_level.txt b/contrib/python/traitlets/py2/.dist-info/top_level.txt deleted file mode 100644 index adfea9c6eb5..00000000000 --- a/contrib/python/traitlets/py2/.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -traitlets diff --git a/contrib/python/traitlets/py2/COPYING.md b/contrib/python/traitlets/py2/COPYING.md deleted file mode 100644 index 39ca730a630..00000000000 --- a/contrib/python/traitlets/py2/COPYING.md +++ /dev/null @@ -1,62 +0,0 @@ -# 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/README.md b/contrib/python/traitlets/py2/README.md deleted file mode 100644 index aa288947efb..00000000000 --- a/contrib/python/traitlets/py2/README.md +++ /dev/null @@ -1,143 +0,0 @@ -# Traitlets - -[](https://travis-ci.org/ipython/traitlets) -[](http://traitlets.readthedocs.org/en/latest/?badge=latest) - -Traitlets is a pure Python library enabling: - - - the enforcement of strong typing for attributes of Python objects - (typed attributes are called "traits"), - - notifications on changes of trait attributes, - - automatic validation and coercion of trait attributes when attempting a - change. - -Its implementation relies on the [descriptor](https://docs.python.org/howto/descriptor.html) -pattern. - -Traitlets powers the configuration system of IPython and Jupyter -and the declarative API of IPython interactive widgets. - -## Installation - -For a local installation, make sure you have -[pip installed](https://pip.pypa.io/en/stable/installing/) and run: - -```bash -pip install traitlets -``` - -For a **development installation**, clone this repository, change into the -`traitlets` root directory, and run pip: - -```bash -git clone https://github.com/ipython/traitlets.git -cd traitlets -pip install -e . -``` - -## Running the tests - -```bash -pip install "traitlets[test]" -py.test traitlets -``` - -## Usage - -Any class with trait attributes must inherit from `HasTraits`. -For the list of available trait types and their properties, see the -[Trait Types](https://traitlets.readthedocs.io/en/latest/trait_types.html) -section of the documentation. - -### Dynamic default values - -To calculate a default value dynamically, decorate a method of your class with -`@default({traitname})`. This method will be called on the instance, and -should return the default value. In this example, the `_username_default` -method is decorated with `@default('username')`: - -```Python -import getpass -from traitlets import HasTraits, Unicode, default - -class Identity(HasTraits): - username = Unicode() - - @default('username') - def _username_default(self): - return getpass.getuser() -``` - -### Callbacks when a trait attribute changes - -When a trait changes, an application can follow this trait change with -additional actions. - -To do something when a trait attribute is changed, decorate a method with -[`traitlets.observe()`](https://traitlets.readthedocs.io/en/latest/api.html?highlight=observe#traitlets.observe). -The method will be called with a single argument, a dictionary which contains -an owner, new value, old value, name of the changed trait, and the event type. - -In this example, the `_num_changed` method is decorated with ``@observe(`num`)``: - -```Python -from traitlets import HasTraits, Integer, observe - -class TraitletsExample(HasTraits): - num = Integer(5, help="a number").tag(config=True) - - @observe('num') - def _num_changed(self, change): - print("{name} changed from {old} to {new}".format(**change)) -``` - -and is passed the following dictionary when called: - -```Python -{ - 'owner': object, # The HasTraits instance - 'new': 6, # The new value - 'old': 5, # The old value - 'name': "foo", # The name of the changed trait - 'type': 'change', # The event type of the notification, usually 'change' -} -``` - -### Validation and coercion - -Each trait type (`Int`, `Unicode`, `Dict` etc.) may have its own validation or -coercion logic. In addition, we can register custom cross-validators -that may depend on the state of other attributes. For example: - -```Python -from traitlets import HasTraits, TraitError, Int, Bool, validate - -class Parity(HasTraits): - value = Int() - parity = Int() - - @validate('value') - def _valid_value(self, proposal): - if proposal['value'] % 2 != self.parity: - raise TraitError('value and parity should be consistent') - return proposal['value'] - - @validate('parity') - def _valid_parity(self, proposal): - parity = proposal['value'] - if parity not in [0, 1]: - raise TraitError('parity should be 0 or 1') - if self.value % 2 != parity: - raise TraitError('value and parity should be consistent') - return proposal['value'] - -parity_check = Parity(value=2) - -# Changing required parity and value together while holding cross validation -with parity_check.hold_trait_notifications(): - parity_check.value = 1 - parity_check.parity = 1 -``` - -However, we **recommend** that custom cross-validators don't modify the state -of the HasTraits instance. diff --git a/contrib/python/traitlets/py2/traitlets/__init__.py b/contrib/python/traitlets/py2/traitlets/__init__.py deleted file mode 100644 index b609adb565d..00000000000 --- a/contrib/python/traitlets/py2/traitlets/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index ed16b3c1e12..00000000000 --- a/contrib/python/traitlets/py2/traitlets/_version.py +++ /dev/null @@ -1,2 +0,0 @@ -version_info = (4, 3, 3) -__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 deleted file mode 100644 index 0ae7d63171d..00000000000 --- a/contrib/python/traitlets/py2/traitlets/config/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# 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 deleted file mode 100644 index d3a4c45e774..00000000000 --- a/contrib/python/traitlets/py2/traitlets/config/application.py +++ /dev/null @@ -1,711 +0,0 @@ -# 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 -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 ( - 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 - -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 -#----------------------------------------------------------------------------- - - - -_envvar = os.environ.get('TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR','') -if _envvar.lower() in {'1','true'}: - TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR = True -elif _envvar.lower() in {'0','false',''} : - TRAITLETS_APPLICATION_RAISE_CONFIG_FILE_ERROR = False -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() - - # 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.""" - new = change.new - if isinstance(new, six.string_types): - new = getattr(logging, new) - self.log_level = new - self.log.setLevel(new) - - _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 - 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""" - 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) - 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()) - - 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. - """ - ) - - _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. - cls = self.__class__ - if cls not in self.classes: - if self.classes is cls.classes: - # class attr, assign instead of insert - cls.classes = [cls] + self.classes - 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:') - 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 - - 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 = [] - 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('') - 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) - - if isinstance(subapp, six.string_types): - 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 = {} - 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 = {} - for key, (flagdict, help) in self.flags.items(): - 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) - 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 - 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: - log.debug("Looking for %s in %s", basefilename, path or os.getcwd()) - jsonloader = cls.json_config_loader_class(basefilename+'.json', path=path, log=log) - loaded = [] - filenames = [] - 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 - 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: - for filename, earlier_config in zip(filenames, loaded): - collisions = earlier_config.collisions(config) - if collisions and log: - log.warning("Collisions detected in {0} and {1} config files." - " {1} has higher priority: {2}".format( - filename, loader.full_filename, json.dumps(collisions, indent=2), - )) - 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) - 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, - ): - new_config.merge(config) - if filename not in self._loaded_config_files: # only add to list of loaded files if not previously loaded - self._loaded_config_files.append(filename) - # 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. - - Thus, produced sample config-file will contain all classes - on which a trait-value may be overridden: - - - either on the class owning the trait, - - or on its subclasses, even if those subclasses do not define - any traits themselves. - """ - cls_to_config = OrderedDict( (cls, bool(cls.class_own_traits(config=True))) - for cls - in self._classes_inc_parents()) - - def is_any_parent_included(cls): - return any(b in cls_to_config and cls_to_config[b] for b in cls.__bases__) - - ## Mark "empty" classes for inclusion if their parents own-traits, - # and loop until no more classes gets marked. - # - while True: - to_incl_orig = cls_to_config.copy() - cls_to_config = OrderedDict( (cls, inc_yes or is_any_parent_included(cls)) - for cls, inc_yes - in cls_to_config.items()) - if cls_to_config == to_incl_orig: - break - for cl, inc_yes in cls_to_config.items(): - 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('') - 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() diff --git a/contrib/python/traitlets/py2/traitlets/config/configurable.py b/contrib/python/traitlets/py2/traitlets/config/configurable.py deleted file mode 100644 index 1174fcf0177..00000000000 --- a/contrib/python/traitlets/py2/traitlets/config/configurable.py +++ /dev/null @@ -1,432 +0,0 @@ -# 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 -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(): - 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)) - elif not _is_section_key(name) and not isinstance(config_value, Config): - 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) - msg = u"Config option `{option}` not recognized by `{klass}`.".format( - option=name, klass=self.__class__.__name__) - - if len(matches) == 1: - msg += u" Did you mean `{matches}`?".format(matches=matches[0]) - 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() - self._load_config(change.new, traits=traits, section_names=section_names) - - 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, - # that self.config prior to update_config was not modified in-place. - # For backward-compatibility, we must ensure that self.config - # is a new object and not modified in-place, - # but config consumers should not rely on this behavior. - self.config = deepcopy(self.config) - # load config - self._load_config(config) - # merge it into self.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)) - - return '## ' + s.replace('\n', '\n# ') - - # 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 - 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)) - 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 - - - diff --git a/contrib/python/traitlets/py2/traitlets/config/loader.py b/contrib/python/traitlets/py2/traitlets/config/loader.py deleted file mode 100644 index 803b36276f2..00000000000 --- a/contrib/python/traitlets/py2/traitlets/config/loader.py +++ /dev/null @@ -1,857 +0,0 @@ -# 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 = {} - 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): - """A JSON file loader for config - - Can also act as a context manager that rewrite the configuration file to disk on exit. - - Example:: - - with JSONFileConfigLoader('myapp.json','/home/jupyter/configurations/') as c: - c.MyNewConfigurable.new_value = 'Updated' - - """ - - 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. - - In case of any error, we do not want to write the potentially broken - configuration to disk. - """ - self.config.version = 1 - json_config = json.dumps(self.config, indent=2) - with open(self.full_filename, 'w') as f: - f.write(json_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: - 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""" - 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 - for key,value in aliases.items(): - 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: - 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 = [] - - 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 diff --git a/contrib/python/traitlets/py2/traitlets/config/manager.py b/contrib/python/traitlets/py2/traitlets/config/manager.py deleted file mode 100644 index 5e5ebde9af3..00000000000 --- a/contrib/python/traitlets/py2/traitlets/config/manager.py +++ /dev/null @@ -1,88 +0,0 @@ -"""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 diff --git a/contrib/python/traitlets/py2/traitlets/config/tests/__init__.py b/contrib/python/traitlets/py2/traitlets/config/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 --- a/contrib/python/traitlets/py2/traitlets/config/tests/__init__.py +++ /dev/null diff --git a/contrib/python/traitlets/py2/traitlets/config/tests/test_application.py b/contrib/python/traitlets/py2/traitlets/config/tests/test_application.py deleted file mode 100644 index 39bd9b786e8..00000000000 --- a/contrib/python/traitlets/py2/traitlets/config/tests/test_application.py +++ /dev/null @@ -1,421 +0,0 @@ -# coding: utf-8 -""" -Tests for traitlets.config.application.Application -""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import json -import logging -import os -from io import StringIO -from unittest import TestCase - -try: - from unittest import mock -except ImportError: - import mock - -pjoin = os.path.join - -from pytest import mark - -from traitlets.config.configurable import Configurable -from traitlets.config.loader import Config -from traitlets.tests.utils import check_help_output, check_help_all_output - -from traitlets.config.application import ( - Application -) - -from ipython_genutils.tempdir import TemporaryDirectory -from traitlets.traitlets import ( - Bool, Unicode, Integer, List, Dict -) - - -class Foo(Configurable): - - i = Integer(0, help="The integer i.").tag(config=True) - j = Integer(1, help="The integer j.").tag(config=True) - name = Unicode(u'Brian', help="First name.").tag(config=True) - - -class Bar(Configurable): - - b = Integer(0, help="The integer b.").tag(config=True) - enabled = Bool(True, help="Enable bar.").tag(config=True) - - -class MyApp(Application): - - name = Unicode(u'myapp') - running = Bool(False, help="Is the app running?").tag(config=True) - classes = List([Bar, Foo]) - config_file = Unicode(u'', help="Load this config file").tag(config=True) - - warn_tpyo = Unicode(u"yes the name is wrong on purpose", config=True, - help="Should print a warning if `MyApp.warn-typo=...` command is passed") - - aliases = Dict({ - 'i' : 'Foo.i', - 'j' : 'Foo.j', - 'name' : 'Foo.name', - 'enabled' : 'Bar.enabled', - 'log-level' : 'Application.log_level', - }) - - flags = Dict(dict(enable=({'Bar': {'enabled' : True}}, "Set Bar.enabled to True"), - disable=({'Bar': {'enabled' : False}}, "Set Bar.enabled to False"), - crit=({'Application' : {'log_level' : logging.CRITICAL}}, - "set level=CRITICAL"), - )) - - def init_foo(self): - self.foo = Foo(parent=self) - - def init_bar(self): - self.bar = Bar(parent=self) - - -class TestApplication(TestCase): - - def test_log(self): - stream = StringIO() - app = MyApp(log_level=logging.INFO) - handler = logging.StreamHandler(stream) - # trigger reconstruction of the log formatter - app.log.handlers = [handler] - app.log_format = "%(message)s" - app.log_datefmt = "%Y-%m-%d %H:%M" - app.log.info("hello") - assert "hello" in stream.getvalue() - - def test_basic(self): - app = MyApp() - self.assertEqual(app.name, u'myapp') - self.assertEqual(app.running, False) - self.assertEqual(app.classes, [MyApp,Bar,Foo]) - self.assertEqual(app.config_file, u'') - - def test_config(self): - app = MyApp() - app.parse_command_line(["--i=10","--Foo.j=10","--enabled=False","--log-level=50"]) - config = app.config - self.assertEqual(config.Foo.i, 10) - self.assertEqual(config.Foo.j, 10) - self.assertEqual(config.Bar.enabled, False) - self.assertEqual(config.MyApp.log_level,50) - - def test_config_propagation(self): - app = MyApp() - app.parse_command_line(["--i=10","--Foo.j=10","--enabled=False","--log-level=50"]) - app.init_foo() - app.init_bar() - self.assertEqual(app.foo.i, 10) - self.assertEqual(app.foo.j, 10) - self.assertEqual(app.bar.enabled, False) - - def test_cli_priority(self): - """Test that loading config files does not override CLI options""" - name = 'config.py' - class TestApp(Application): - value = Unicode().tag(config=True) - config_file_loaded = Bool().tag(config=True) - aliases = {'v': 'TestApp.value'} - app = TestApp() - with TemporaryDirectory() as td: - config_file = pjoin(td, name) - with open(config_file, 'w') as f: - f.writelines([ - "c.TestApp.value = 'config file'\n", - "c.TestApp.config_file_loaded = True\n" - ]) - - app.parse_command_line(['--v=cli']) - assert 'value' in app.config.TestApp - assert app.config.TestApp.value == 'cli' - assert app.value == 'cli' - - app.load_config_file(name, path=[td]) - assert app.config_file_loaded - assert app.config.TestApp.value == 'cli' - assert app.value == 'cli' - - def test_ipython_cli_priority(self): - # this test is almost entirely redundant with above, - # but we can keep it around in case of subtle issues creeping into - # the exact sequence IPython follows. - name = 'config.py' - class TestApp(Application): - value = Unicode().tag(config=True) - config_file_loaded = Bool().tag(config=True) - aliases = {'v': 'TestApp.value'} - app = TestApp() - with TemporaryDirectory() as td: - config_file = pjoin(td, name) - with open(config_file, 'w') as f: - f.writelines([ - "c.TestApp.value = 'config file'\n", - "c.TestApp.config_file_loaded = True\n" - ]) - # follow IPython's config-loading sequence to ensure CLI priority is preserved - app.parse_command_line(['--v=cli']) - # this is where IPython makes a mistake: - # it assumes app.config will not be modified, - # and storing a reference is storing a copy - cli_config = app.config - assert 'value' in app.config.TestApp - assert app.config.TestApp.value == 'cli' - assert app.value == 'cli' - app.load_config_file(name, path=[td]) - assert app.config_file_loaded - # enforce cl-opts override config file opts: - # this is where IPython makes a mistake: it assumes - # that cl_config is a different object, but it isn't. - app.update_config(cli_config) - assert app.config.TestApp.value == 'cli' - assert app.value == 'cli' - - def test_flags(self): - app = MyApp() - app.parse_command_line(["--disable"]) - app.init_bar() - self.assertEqual(app.bar.enabled, False) - app.parse_command_line(["--enable"]) - app.init_bar() - self.assertEqual(app.bar.enabled, True) - - def test_aliases(self): - app = MyApp() - app.parse_command_line(["--i=5", "--j=10"]) - app.init_foo() - self.assertEqual(app.foo.i, 5) - app.init_foo() - self.assertEqual(app.foo.j, 10) - - def test_flag_clobber(self): - """test that setting flags doesn't clobber existing settings""" - app = MyApp() - app.parse_command_line(["--Bar.b=5", "--disable"]) - app.init_bar() - self.assertEqual(app.bar.enabled, False) - self.assertEqual(app.bar.b, 5) - app.parse_command_line(["--enable", "--Bar.b=10"]) - app.init_bar() - self.assertEqual(app.bar.enabled, True) - self.assertEqual(app.bar.b, 10) - - def test_warn_autocorrect(self): - stream = StringIO() - app = MyApp(log_level=logging.INFO) - app.log.handlers = [logging.StreamHandler(stream)] - - cfg = Config() - cfg.MyApp.warn_typo = "WOOOO" - app.config = cfg - - self.assertIn("warn_typo", stream.getvalue()) - self.assertIn("warn_tpyo", stream.getvalue()) - - - def test_flatten_flags(self): - cfg = Config() - cfg.MyApp.log_level = logging.WARN - app = MyApp() - app.update_config(cfg) - self.assertEqual(app.log_level, logging.WARN) - self.assertEqual(app.config.MyApp.log_level, logging.WARN) - app.initialize(["--crit"]) - self.assertEqual(app.log_level, logging.CRITICAL) - # this would be app.config.Application.log_level if it failed: - self.assertEqual(app.config.MyApp.log_level, logging.CRITICAL) - - def test_flatten_aliases(self): - cfg = Config() - cfg.MyApp.log_level = logging.WARN - app = MyApp() - app.update_config(cfg) - self.assertEqual(app.log_level, logging.WARN) - self.assertEqual(app.config.MyApp.log_level, logging.WARN) - app.initialize(["--log-level", "CRITICAL"]) - self.assertEqual(app.log_level, logging.CRITICAL) - # this would be app.config.Application.log_level if it failed: - self.assertEqual(app.config.MyApp.log_level, "CRITICAL") - - def test_extra_args(self): - app = MyApp() - app.parse_command_line(["--Bar.b=5", 'extra', "--disable", 'args']) - app.init_bar() - self.assertEqual(app.bar.enabled, False) - self.assertEqual(app.bar.b, 5) - self.assertEqual(app.extra_args, ['extra', 'args']) - app = MyApp() - app.parse_command_line(["--Bar.b=5", '--', 'extra', "--disable", 'args']) - app.init_bar() - self.assertEqual(app.bar.enabled, True) - self.assertEqual(app.bar.b, 5) - self.assertEqual(app.extra_args, ['extra', '--disable', 'args']) - - def test_unicode_argv(self): - app = MyApp() - app.parse_command_line(['ünîcødé']) - - def test_document_config_option(self): - app = MyApp() - app.document_config_options() - - def test_generate_config_file(self): - app = MyApp() - assert 'The integer b.' in app.generate_config_file() - - def test_generate_config_file_classes_to_include(self): - class NoTraits(Foo, Bar): - pass - - app = MyApp() - app.classes.append(NoTraits) - conf_txt = app.generate_config_file() - self.assertIn('The integer b.', conf_txt) - self.assertIn('# Bar(Configurable)', conf_txt) - self.assertIn('# Foo(Configurable)', conf_txt) - self.assertNotIn('# Configurable', conf_txt) - self.assertIn('# NoTraits(Foo,Bar)', conf_txt) - - def test_multi_file(self): - app = MyApp() - app.log = logging.getLogger() - name = 'config.py' - with TemporaryDirectory('_1') as td1: - with open(pjoin(td1, name), 'w') as f1: - f1.write("get_config().MyApp.Bar.b = 1") - with TemporaryDirectory('_2') as td2: - with open(pjoin(td2, name), 'w') as f2: - f2.write("get_config().MyApp.Bar.b = 2") - app.load_config_file(name, path=[td2, td1]) - app.init_bar() - self.assertEqual(app.bar.b, 2) - app.load_config_file(name, path=[td1, td2]) - app.init_bar() - self.assertEqual(app.bar.b, 1) - - @mark.skipif(not hasattr(TestCase, 'assertLogs'), reason='requires TestCase.assertLogs') - def test_log_collisions(self): - app = MyApp() - app.log = logging.getLogger() - app.log.setLevel(logging.INFO) - name = 'config' - with TemporaryDirectory('_1') as td: - with open(pjoin(td, name + '.py'), 'w') as f: - f.write("get_config().Bar.b = 1") - with open(pjoin(td, name + '.json'), 'w') as f: - json.dump({ - 'Bar': { - 'b': 2 - } - }, f) - with self.assertLogs(app.log, logging.WARNING) as captured: - app.load_config_file(name, path=[td]) - app.init_bar() - assert app.bar.b == 2 - output = '\n'.join(captured.output) - assert 'Collision' in output - assert '1 ignored, using 2' in output - assert pjoin(td, name + '.py') in output - assert pjoin(td, name + '.json') in output - - @mark.skipif(not hasattr(TestCase, 'assertLogs'), reason='requires TestCase.assertLogs') - def test_log_bad_config(self): - app = MyApp() - app.log = logging.getLogger() - name = 'config.py' - with TemporaryDirectory() as td: - with open(pjoin(td, name), 'w') as f: - f.write("syntax error()") - with self.assertLogs(app.log, logging.ERROR) as captured: - app.load_config_file(name, path=[td]) - output = '\n'.join(captured.output) - self.assertIn('SyntaxError', output) - - def test_raise_on_bad_config(self): - app = MyApp() - app.raise_config_file_errors = True - app.log = logging.getLogger() - name = 'config.py' - with TemporaryDirectory() as td: - with open(pjoin(td, name), 'w') as f: - f.write("syntax error()") - with self.assertRaises(SyntaxError): - app.load_config_file(name, path=[td]) - - def test_loaded_config_files(self): - app = MyApp() - app.log = logging.getLogger() - name = 'config.py' - with TemporaryDirectory('_1') as td1: - config_file = pjoin(td1, name) - with open(config_file, 'w') as f: - f.writelines([ - "c.MyApp.running = True\n" - ]) - - app.load_config_file(name, path=[td1]) - self.assertEqual(len(app.loaded_config_files), 1) - self.assertEquals(app.loaded_config_files[0], config_file) - - app.start() - self.assertEqual(app.running, True) - - # emulate an app that allows dynamic updates and update config file - with open(config_file, 'w') as f: - f.writelines([ - "c.MyApp.running = False\n" - ]) - - # reload and verify update, and that loaded_configs was not increased - app.load_config_file(name, path=[td1]) - self.assertEqual(len(app.loaded_config_files), 1) - self.assertEqual(app.running, False) - - # Attempt to update, ensure error... - with self.assertRaises(AttributeError): - app.loaded_config_files = "/foo" - - # ensure it can't be udpated via append - app.loaded_config_files.append("/bar") - self.assertEqual(len(app.loaded_config_files), 1) - - # repeat to ensure no unexpected changes occurred - app.load_config_file(name, path=[td1]) - self.assertEqual(len(app.loaded_config_files), 1) - self.assertEqual(app.running, False) - - -class DeprecatedApp(Application): - override_called = False - parent_called = False - def _config_changed(self, name, old, new): - self.override_called = True - def _capture(*args): - self.parent_called = True - with mock.patch.object(self.log, 'debug', _capture): - super(DeprecatedApp, self)._config_changed(name, old, new) - - -def test_deprecated_notifier(): - app = DeprecatedApp() - assert not app.override_called - assert not app.parent_called - app.config = Config({'A': {'b': 'c'}}) - assert app.override_called - assert app.parent_called - - -def test_help_output(): - check_help_output(__name__) - check_help_all_output(__name__) - -if __name__ == '__main__': - # for test_help_output: - MyApp.launch_instance()
\ No newline at end of file diff --git a/contrib/python/traitlets/py2/traitlets/config/tests/test_configurable.py b/contrib/python/traitlets/py2/traitlets/config/tests/test_configurable.py deleted file mode 100644 index 9fbdb7209dc..00000000000 --- a/contrib/python/traitlets/py2/traitlets/config/tests/test_configurable.py +++ /dev/null @@ -1,459 +0,0 @@ -# encoding: utf-8 -"""Tests for traitlets.config.configurable""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import logging -from unittest import TestCase - -from pytest import mark - -from traitlets.config.configurable import ( - Configurable, - LoggingConfigurable, - SingletonConfigurable, -) - -from traitlets.traitlets import ( - Integer, Float, Unicode, List, Dict, Set, - _deprecations_shown, -) - -from traitlets.config.loader import Config -from six import PY3 - -from ...tests._warnings import expected_warnings - -class MyConfigurable(Configurable): - a = Integer(1, help="The integer a.").tag(config=True) - b = Float(1.0, help="The integer b.").tag(config=True) - c = Unicode('no config') - - -mc_help=u"""MyConfigurable options ----------------------- ---MyConfigurable.a=<Integer> - Default: 1 - The integer a. ---MyConfigurable.b=<Float> - Default: 1.0 - The integer b.""" - -mc_help_inst=u"""MyConfigurable options ----------------------- ---MyConfigurable.a=<Integer> - Current: 5 - The integer a. ---MyConfigurable.b=<Float> - Current: 4.0 - The integer b.""" - -# On Python 3, the Integer trait is a synonym for Int -if PY3: - mc_help = mc_help.replace(u"<Integer>", u"<Int>") - mc_help_inst = mc_help_inst.replace(u"<Integer>", u"<Int>") - -class Foo(Configurable): - a = Integer(0, help="The integer a.").tag(config=True) - b = Unicode('nope').tag(config=True) - - -class Bar(Foo): - b = Unicode('gotit', help="The string b.").tag(config=False) - c = Float(help="The string c.").tag(config=True) - - -class TestConfigurable(TestCase): - - def test_default(self): - c1 = Configurable() - c2 = Configurable(config=c1.config) - c3 = Configurable(config=c2.config) - self.assertEqual(c1.config, c2.config) - self.assertEqual(c2.config, c3.config) - - def test_custom(self): - config = Config() - config.foo = 'foo' - config.bar = 'bar' - c1 = Configurable(config=config) - c2 = Configurable(config=c1.config) - c3 = Configurable(config=c2.config) - self.assertEqual(c1.config, config) - self.assertEqual(c2.config, config) - self.assertEqual(c3.config, config) - # Test that copies are not made - self.assertTrue(c1.config is config) - self.assertTrue(c2.config is config) - self.assertTrue(c3.config is config) - self.assertTrue(c1.config is c2.config) - self.assertTrue(c2.config is c3.config) - - def test_inheritance(self): - config = Config() - config.MyConfigurable.a = 2 - config.MyConfigurable.b = 2.0 - c1 = MyConfigurable(config=config) - c2 = MyConfigurable(config=c1.config) - self.assertEqual(c1.a, config.MyConfigurable.a) - self.assertEqual(c1.b, config.MyConfigurable.b) - self.assertEqual(c2.a, config.MyConfigurable.a) - self.assertEqual(c2.b, config.MyConfigurable.b) - - def test_parent(self): - config = Config() - config.Foo.a = 10 - config.Foo.b = "wow" - config.Bar.b = 'later' - config.Bar.c = 100.0 - f = Foo(config=config) - with expected_warnings(['`b` not recognized']): - b = Bar(config=f.config) - self.assertEqual(f.a, 10) - self.assertEqual(f.b, 'wow') - self.assertEqual(b.b, 'gotit') - self.assertEqual(b.c, 100.0) - - def test_override1(self): - config = Config() - config.MyConfigurable.a = 2 - config.MyConfigurable.b = 2.0 - c = MyConfigurable(a=3, config=config) - self.assertEqual(c.a, 3) - self.assertEqual(c.b, config.MyConfigurable.b) - self.assertEqual(c.c, 'no config') - - def test_override2(self): - config = Config() - config.Foo.a = 1 - config.Bar.b = 'or' # Up above b is config=False, so this won't do it. - config.Bar.c = 10.0 - with expected_warnings(['`b` not recognized']): - c = Bar(config=config) - self.assertEqual(c.a, config.Foo.a) - self.assertEqual(c.b, 'gotit') - self.assertEqual(c.c, config.Bar.c) - with expected_warnings(['`b` not recognized']): - c = Bar(a=2, b='and', c=20.0, config=config) - self.assertEqual(c.a, 2) - self.assertEqual(c.b, 'and') - self.assertEqual(c.c, 20.0) - - def test_help(self): - self.assertEqual(MyConfigurable.class_get_help(), mc_help) - - def test_help_inst(self): - inst = MyConfigurable(a=5, b=4) - self.assertEqual(MyConfigurable.class_get_help(inst), mc_help_inst) - - -class TestSingletonConfigurable(TestCase): - - def test_instance(self): - class Foo(SingletonConfigurable): pass - self.assertEqual(Foo.initialized(), False) - foo = Foo.instance() - self.assertEqual(Foo.initialized(), True) - self.assertEqual(foo, Foo.instance()) - self.assertEqual(SingletonConfigurable._instance, None) - - def test_inheritance(self): - class Bar(SingletonConfigurable): pass - class Bam(Bar): pass - self.assertEqual(Bar.initialized(), False) - self.assertEqual(Bam.initialized(), False) - bam = Bam.instance() - bam == Bar.instance() - self.assertEqual(Bar.initialized(), True) - self.assertEqual(Bam.initialized(), True) - self.assertEqual(bam, Bam._instance) - self.assertEqual(bam, Bar._instance) - self.assertEqual(SingletonConfigurable._instance, None) - - -class MyParent(Configurable): - pass - -class MyParent2(MyParent): - pass - -class TestParentConfigurable(TestCase): - - def test_parent_config(self): - cfg = Config({ - 'MyParent' : { - 'MyConfigurable' : { - 'b' : 2.0, - } - } - }) - parent = MyParent(config=cfg) - myc = MyConfigurable(parent=parent) - self.assertEqual(myc.b, parent.config.MyParent.MyConfigurable.b) - - def test_parent_inheritance(self): - cfg = Config({ - 'MyParent' : { - 'MyConfigurable' : { - 'b' : 2.0, - } - } - }) - parent = MyParent2(config=cfg) - myc = MyConfigurable(parent=parent) - self.assertEqual(myc.b, parent.config.MyParent.MyConfigurable.b) - - def test_multi_parent(self): - cfg = Config({ - 'MyParent2' : { - 'MyParent' : { - 'MyConfigurable' : { - 'b' : 2.0, - } - }, - # this one shouldn't count - 'MyConfigurable' : { - 'b' : 3.0, - }, - } - }) - parent2 = MyParent2(config=cfg) - parent = MyParent(parent=parent2) - myc = MyConfigurable(parent=parent) - self.assertEqual(myc.b, parent.config.MyParent2.MyParent.MyConfigurable.b) - - def test_parent_priority(self): - cfg = Config({ - 'MyConfigurable' : { - 'b' : 2.0, - }, - 'MyParent' : { - 'MyConfigurable' : { - 'b' : 3.0, - } - }, - 'MyParent2' : { - 'MyConfigurable' : { - 'b' : 4.0, - } - } - }) - parent = MyParent2(config=cfg) - myc = MyConfigurable(parent=parent) - self.assertEqual(myc.b, parent.config.MyParent2.MyConfigurable.b) - - def test_multi_parent_priority(self): - cfg = Config({ - 'MyConfigurable' : { - 'b' : 2.0, - }, - 'MyParent' : { - 'MyConfigurable' : { - 'b' : 3.0, - } - }, - 'MyParent2' : { - 'MyConfigurable' : { - 'b' : 4.0, - } - }, - 'MyParent2' : { - 'MyParent' : { - 'MyConfigurable' : { - 'b' : 5.0, - } - } - } - }) - parent2 = MyParent2(config=cfg) - parent = MyParent2(parent=parent2) - myc = MyConfigurable(parent=parent) - self.assertEqual(myc.b, parent.config.MyParent2.MyParent.MyConfigurable.b) - -class Containers(Configurable): - lis = List().tag(config=True) - def _lis_default(self): - return [-1] - - s = Set().tag(config=True) - def _s_default(self): - return {'a'} - - d = Dict().tag(config=True) - def _d_default(self): - return {'a' : 'b'} - -class TestConfigContainers(TestCase): - def test_extend(self): - c = Config() - c.Containers.lis.extend(list(range(5))) - obj = Containers(config=c) - self.assertEqual(obj.lis, list(range(-1,5))) - - def test_insert(self): - c = Config() - c.Containers.lis.insert(0, 'a') - c.Containers.lis.insert(1, 'b') - obj = Containers(config=c) - self.assertEqual(obj.lis, ['a', 'b', -1]) - - def test_prepend(self): - c = Config() - c.Containers.lis.prepend([1,2]) - c.Containers.lis.prepend([2,3]) - obj = Containers(config=c) - self.assertEqual(obj.lis, [2,3,1,2,-1]) - - def test_prepend_extend(self): - c = Config() - c.Containers.lis.prepend([1,2]) - c.Containers.lis.extend([2,3]) - obj = Containers(config=c) - self.assertEqual(obj.lis, [1,2,-1,2,3]) - - def test_append_extend(self): - c = Config() - c.Containers.lis.append([1,2]) - c.Containers.lis.extend([2,3]) - obj = Containers(config=c) - self.assertEqual(obj.lis, [-1,[1,2],2,3]) - - def test_extend_append(self): - c = Config() - c.Containers.lis.extend([2,3]) - c.Containers.lis.append([1,2]) - obj = Containers(config=c) - self.assertEqual(obj.lis, [-1,2,3,[1,2]]) - - def test_insert_extend(self): - c = Config() - c.Containers.lis.insert(0, 1) - c.Containers.lis.extend([2,3]) - obj = Containers(config=c) - self.assertEqual(obj.lis, [1,-1,2,3]) - - def test_set_update(self): - c = Config() - c.Containers.s.update({0,1,2}) - c.Containers.s.update({3}) - obj = Containers(config=c) - self.assertEqual(obj.s, {'a', 0, 1, 2, 3}) - - def test_dict_update(self): - c = Config() - c.Containers.d.update({'c' : 'd'}) - c.Containers.d.update({'e' : 'f'}) - obj = Containers(config=c) - self.assertEqual(obj.d, {'a':'b', 'c':'d', 'e':'f'}) - - def test_update_twice(self): - c = Config() - c.MyConfigurable.a = 5 - m = MyConfigurable(config=c) - self.assertEqual(m.a, 5) - - c2 = Config() - c2.MyConfigurable.a = 10 - m.update_config(c2) - self.assertEqual(m.a, 10) - - c2.MyConfigurable.a = 15 - m.update_config(c2) - self.assertEqual(m.a, 15) - - def test_update_self(self): - """update_config with same config object still triggers config_changed""" - c = Config() - c.MyConfigurable.a = 5 - m = MyConfigurable(config=c) - self.assertEqual(m.a, 5) - c.MyConfigurable.a = 10 - m.update_config(c) - self.assertEqual(m.a, 10) - - def test_config_default(self): - class SomeSingleton(SingletonConfigurable): - pass - - class DefaultConfigurable(Configurable): - a = Integer().tag(config=True) - def _config_default(self): - if SomeSingleton.initialized(): - return SomeSingleton.instance().config - return Config() - - c = Config() - c.DefaultConfigurable.a = 5 - - d1 = DefaultConfigurable() - self.assertEqual(d1.a, 0) - - single = SomeSingleton.instance(config=c) - - d2 = DefaultConfigurable() - self.assertIs(d2.config, single.config) - self.assertEqual(d2.a, 5) - - def test_config_default_deprecated(self): - """Make sure configurables work even with the deprecations in traitlets""" - class SomeSingleton(SingletonConfigurable): - pass - - # reset deprecation limiter - _deprecations_shown.clear() - with expected_warnings([]): - class DefaultConfigurable(Configurable): - a = Integer(config=True) - def _config_default(self): - if SomeSingleton.initialized(): - return SomeSingleton.instance().config - return Config() - - c = Config() - c.DefaultConfigurable.a = 5 - - d1 = DefaultConfigurable() - self.assertEqual(d1.a, 0) - - single = SomeSingleton.instance(config=c) - - d2 = DefaultConfigurable() - self.assertIs(d2.config, single.config) - self.assertEqual(d2.a, 5) - - -class TestLogger(TestCase): - - class A(LoggingConfigurable): - foo = Integer(config=True) - bar = Integer(config=True) - baz = Integer(config=True) - - @mark.skipif(not hasattr(TestCase, 'assertLogs'), reason='requires TestCase.assertLogs') - def test_warn_match(self): - logger = logging.getLogger('test_warn_match') - cfg = Config({'A': {'bat': 5}}) - with self.assertLogs(logger, logging.WARNING) as captured: - a = TestLogger.A(config=cfg, log=logger) - - output = '\n'.join(captured.output) - self.assertIn('Did you mean one of: `bar, baz`?', output) - self.assertIn('Config option `bat` not recognized by `A`.', output) - - cfg = Config({'A': {'fool': 5}}) - with self.assertLogs(logger, logging.WARNING) as captured: - a = TestLogger.A(config=cfg, log=logger) - - output = '\n'.join(captured.output) - self.assertIn('Config option `fool` not recognized by `A`.', output) - self.assertIn('Did you mean `foo`?', output) - - cfg = Config({'A': {'totally_wrong': 5}}) - with self.assertLogs(logger, logging.WARNING) as captured: - a = TestLogger.A(config=cfg, log=logger) - - output = '\n'.join(captured.output) - self.assertIn('Config option `totally_wrong` not recognized by `A`.', output) - self.assertNotIn('Did you mean', output) - diff --git a/contrib/python/traitlets/py2/traitlets/config/tests/test_loader.py b/contrib/python/traitlets/py2/traitlets/config/tests/test_loader.py deleted file mode 100644 index 50c8659f3c6..00000000000 --- a/contrib/python/traitlets/py2/traitlets/config/tests/test_loader.py +++ /dev/null @@ -1,453 +0,0 @@ -# encoding: utf-8 -"""Tests for traitlets.config.loader""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. - -import copy -import logging -import os -import pickle -import sys -from tempfile import mkstemp -from unittest import TestCase - -from pytest import skip - -from traitlets.config.loader import ( - Config, - LazyConfigValue, - PyFileConfigLoader, - JSONFileConfigLoader, - KeyValueConfigLoader, - ArgParseConfigLoader, - KVArgParseConfigLoader, - ConfigError, -) - - -pyfile = """ -c = get_config() -c.a=10 -c.b=20 -c.Foo.Bar.value=10 -c.Foo.Bam.value=list(range(10)) -c.D.C.value='hi there' -""" - -json1file = """ -{ - "version": 1, - "a": 10, - "b": 20, - "Foo": { - "Bam": { - "value": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] - }, - "Bar": { - "value": 10 - } - }, - "D": { - "C": { - "value": "hi there" - } - } -} -""" - -# should not load -json2file = """ -{ - "version": 2 -} -""" - -import logging -log = logging.getLogger('devnull') -log.setLevel(0) - -class TestFileCL(TestCase): - - def _check_conf(self, config): - self.assertEqual(config.a, 10) - self.assertEqual(config.b, 20) - self.assertEqual(config.Foo.Bar.value, 10) - self.assertEqual(config.Foo.Bam.value, list(range(10))) - self.assertEqual(config.D.C.value, 'hi there') - - def test_python(self): - fd, fname = mkstemp('.py') - f = os.fdopen(fd, 'w') - f.write(pyfile) - f.close() - # Unlink the file - cl = PyFileConfigLoader(fname, log=log) - config = cl.load_config() - self._check_conf(config) - - def test_json(self): - fd, fname = mkstemp('.json') - f = os.fdopen(fd, 'w') - f.write(json1file) - f.close() - # Unlink the file - cl = JSONFileConfigLoader(fname, log=log) - config = cl.load_config() - self._check_conf(config) - - def test_context_manager(self): - - fd, fname = mkstemp('.json') - f = os.fdopen(fd, 'w') - f.write('{}') - f.close() - - cl = JSONFileConfigLoader(fname, log=log) - - value = 'context_manager' - - with cl as c: - c.MyAttr.value = value - - self.assertEqual(cl.config.MyAttr.value, value) - - # check that another loader does see the change - cl2 = JSONFileConfigLoader(fname, log=log) - self.assertEqual(cl.config.MyAttr.value, value) - - def test_json_context_bad_write(self): - fd, fname = mkstemp('.json') - f = os.fdopen(fd, 'w') - f.write('{}') - f.close() - - with JSONFileConfigLoader(fname, log=log) as config: - config.A.b = 1 - - with self.assertRaises(TypeError): - with JSONFileConfigLoader(fname, log=log) as config: - config.A.cant_json = lambda x: x - - loader = JSONFileConfigLoader(fname, log=log) - cfg = loader.load_config() - assert cfg.A.b == 1 - assert 'cant_json' not in cfg.A - - def test_collision(self): - a = Config() - b = Config() - self.assertEqual(a.collisions(b), {}) - a.A.trait1 = 1 - b.A.trait2 = 2 - self.assertEqual(a.collisions(b), {}) - b.A.trait1 = 1 - self.assertEqual(a.collisions(b), {}) - b.A.trait1 = 0 - self.assertEqual(a.collisions(b), { - 'A': { - 'trait1': "1 ignored, using 0", - } - }) - self.assertEqual(b.collisions(a), { - 'A': { - 'trait1': "0 ignored, using 1", - } - }) - a.A.trait2 = 3 - self.assertEqual(b.collisions(a), { - 'A': { - 'trait1': "0 ignored, using 1", - 'trait2': "2 ignored, using 3", - } - }) - - def test_v2raise(self): - fd, fname = mkstemp('.json') - f = os.fdopen(fd, 'w') - f.write(json2file) - f.close() - # Unlink the file - cl = JSONFileConfigLoader(fname, log=log) - with self.assertRaises(ValueError): - cl.load_config() - - -class MyLoader1(ArgParseConfigLoader): - def _add_arguments(self, aliases=None, flags=None): - p = self.parser - p.add_argument('-f', '--foo', dest='Global.foo', type=str) - p.add_argument('-b', dest='MyClass.bar', type=int) - p.add_argument('-n', dest='n', action='store_true') - p.add_argument('Global.bam', type=str) - -class MyLoader2(ArgParseConfigLoader): - def _add_arguments(self, aliases=None, flags=None): - subparsers = self.parser.add_subparsers(dest='subparser_name') - subparser1 = subparsers.add_parser('1') - subparser1.add_argument('-x',dest='Global.x') - subparser2 = subparsers.add_parser('2') - subparser2.add_argument('y') - -class TestArgParseCL(TestCase): - - def test_basic(self): - cl = MyLoader1() - config = cl.load_config('-f hi -b 10 -n wow'.split()) - self.assertEqual(config.Global.foo, 'hi') - self.assertEqual(config.MyClass.bar, 10) - self.assertEqual(config.n, True) - self.assertEqual(config.Global.bam, 'wow') - config = cl.load_config(['wow']) - self.assertEqual(list(config.keys()), ['Global']) - self.assertEqual(list(config.Global.keys()), ['bam']) - self.assertEqual(config.Global.bam, 'wow') - - def test_add_arguments(self): - cl = MyLoader2() - config = cl.load_config('2 frobble'.split()) - self.assertEqual(config.subparser_name, '2') - self.assertEqual(config.y, 'frobble') - config = cl.load_config('1 -x frobble'.split()) - self.assertEqual(config.subparser_name, '1') - self.assertEqual(config.Global.x, 'frobble') - - def test_argv(self): - cl = MyLoader1(argv='-f hi -b 10 -n wow'.split()) - config = cl.load_config() - self.assertEqual(config.Global.foo, 'hi') - self.assertEqual(config.MyClass.bar, 10) - self.assertEqual(config.n, True) - self.assertEqual(config.Global.bam, 'wow') - - -class TestKeyValueCL(TestCase): - klass = KeyValueConfigLoader - - def test_eval(self): - cl = self.klass(log=log) - config = cl.load_config('--Class.str_trait=all --Class.int_trait=5 --Class.list_trait=["hello",5]'.split()) - self.assertEqual(config.Class.str_trait, 'all') - self.assertEqual(config.Class.int_trait, 5) - self.assertEqual(config.Class.list_trait, ["hello", 5]) - - def test_basic(self): - cl = self.klass(log=log) - argv = [ '--' + s[2:] for s in pyfile.split('\n') if s.startswith('c.') ] - print(argv) - config = cl.load_config(argv) - self.assertEqual(config.a, 10) - self.assertEqual(config.b, 20) - self.assertEqual(config.Foo.Bar.value, 10) - # non-literal expressions are not evaluated - self.assertEqual(config.Foo.Bam.value, 'list(range(10))') - self.assertEqual(config.D.C.value, 'hi there') - - def test_expanduser(self): - cl = self.klass(log=log) - argv = ['--a=~/1/2/3', '--b=~', '--c=~/', '--d="~/"'] - config = cl.load_config(argv) - self.assertEqual(config.a, os.path.expanduser('~/1/2/3')) - self.assertEqual(config.b, os.path.expanduser('~')) - self.assertEqual(config.c, os.path.expanduser('~/')) - self.assertEqual(config.d, '~/') - - def test_extra_args(self): - cl = self.klass(log=log) - config = cl.load_config(['--a=5', 'b', '--c=10', 'd']) - self.assertEqual(cl.extra_args, ['b', 'd']) - self.assertEqual(config.a, 5) - self.assertEqual(config.c, 10) - config = cl.load_config(['--', '--a=5', '--c=10']) - self.assertEqual(cl.extra_args, ['--a=5', '--c=10']) - - def test_unicode_args(self): - cl = self.klass(log=log) - argv = [u'--a=épsîlön'] - config = cl.load_config(argv) - self.assertEqual(config.a, u'épsîlön') - - def test_unicode_bytes_args(self): - uarg = u'--a=é' - try: - barg = uarg.encode(sys.stdin.encoding) - except (TypeError, UnicodeEncodeError): - raise skip("sys.stdin.encoding can't handle 'é'") - - cl = self.klass(log=log) - config = cl.load_config([barg]) - self.assertEqual(config.a, u'é') - - def test_unicode_alias(self): - cl = self.klass(log=log) - argv = [u'--a=épsîlön'] - config = cl.load_config(argv, aliases=dict(a='A.a')) - self.assertEqual(config.A.a, u'épsîlön') - - -class TestArgParseKVCL(TestKeyValueCL): - klass = KVArgParseConfigLoader - - def test_expanduser2(self): - cl = self.klass(log=log) - argv = ['-a', '~/1/2/3', '--b', "'~/1/2/3'"] - config = cl.load_config(argv, aliases=dict(a='A.a', b='A.b')) - self.assertEqual(config.A.a, os.path.expanduser('~/1/2/3')) - self.assertEqual(config.A.b, '~/1/2/3') - - def test_eval(self): - cl = self.klass(log=log) - argv = ['-c', 'a=5'] - config = cl.load_config(argv, aliases=dict(c='A.c')) - self.assertEqual(config.A.c, u"a=5") - - -class TestConfig(TestCase): - - def test_setget(self): - c = Config() - c.a = 10 - self.assertEqual(c.a, 10) - self.assertEqual('b' in c, False) - - def test_auto_section(self): - c = Config() - self.assertNotIn('A', c) - assert not c._has_section('A') - A = c.A - A.foo = 'hi there' - self.assertIn('A', c) - assert c._has_section('A') - self.assertEqual(c.A.foo, 'hi there') - del c.A - self.assertEqual(c.A, Config()) - - def test_merge_doesnt_exist(self): - c1 = Config() - c2 = Config() - c2.bar = 10 - c2.Foo.bar = 10 - c1.merge(c2) - self.assertEqual(c1.Foo.bar, 10) - self.assertEqual(c1.bar, 10) - c2.Bar.bar = 10 - c1.merge(c2) - self.assertEqual(c1.Bar.bar, 10) - - def test_merge_exists(self): - c1 = Config() - c2 = Config() - c1.Foo.bar = 10 - c1.Foo.bam = 30 - c2.Foo.bar = 20 - c2.Foo.wow = 40 - c1.merge(c2) - self.assertEqual(c1.Foo.bam, 30) - self.assertEqual(c1.Foo.bar, 20) - self.assertEqual(c1.Foo.wow, 40) - c2.Foo.Bam.bam = 10 - c1.merge(c2) - self.assertEqual(c1.Foo.Bam.bam, 10) - - def test_deepcopy(self): - c1 = Config() - c1.Foo.bar = 10 - c1.Foo.bam = 30 - c1.a = 'asdf' - c1.b = range(10) - c1.Test.logger = logging.Logger('test') - c1.Test.get_logger = logging.getLogger('test') - c2 = copy.deepcopy(c1) - self.assertEqual(c1, c2) - self.assertTrue(c1 is not c2) - self.assertTrue(c1.Foo is not c2.Foo) - self.assertTrue(c1.Test is not c2.Test) - self.assertTrue(c1.Test.logger is c2.Test.logger) - self.assertTrue(c1.Test.get_logger is c2.Test.get_logger) - - def test_builtin(self): - c1 = Config() - c1.format = "json" - - def test_fromdict(self): - c1 = Config({'Foo' : {'bar' : 1}}) - self.assertEqual(c1.Foo.__class__, Config) - self.assertEqual(c1.Foo.bar, 1) - - def test_fromdictmerge(self): - c1 = Config() - c2 = Config({'Foo' : {'bar' : 1}}) - c1.merge(c2) - self.assertEqual(c1.Foo.__class__, Config) - self.assertEqual(c1.Foo.bar, 1) - - def test_fromdictmerge2(self): - c1 = Config({'Foo' : {'baz' : 2}}) - c2 = Config({'Foo' : {'bar' : 1}}) - c1.merge(c2) - self.assertEqual(c1.Foo.__class__, Config) - self.assertEqual(c1.Foo.bar, 1) - self.assertEqual(c1.Foo.baz, 2) - self.assertNotIn('baz', c2.Foo) - - def test_contains(self): - c1 = Config({'Foo' : {'baz' : 2}}) - c2 = Config({'Foo' : {'bar' : 1}}) - self.assertIn('Foo', c1) - self.assertIn('Foo.baz', c1) - self.assertIn('Foo.bar', c2) - self.assertNotIn('Foo.bar', c1) - - def test_pickle_config(self): - cfg = Config() - cfg.Foo.bar = 1 - pcfg = pickle.dumps(cfg) - cfg2 = pickle.loads(pcfg) - self.assertEqual(cfg2, cfg) - - def test_getattr_section(self): - cfg = Config() - self.assertNotIn('Foo', cfg) - Foo = cfg.Foo - assert isinstance(Foo, Config) - self.assertIn('Foo', cfg) - - def test_getitem_section(self): - cfg = Config() - self.assertNotIn('Foo', cfg) - Foo = cfg['Foo'] - assert isinstance(Foo, Config) - self.assertIn('Foo', cfg) - - def test_getattr_not_section(self): - cfg = Config() - self.assertNotIn('foo', cfg) - foo = cfg.foo - assert isinstance(foo, LazyConfigValue) - self.assertIn('foo', cfg) - - def test_getattr_private_missing(self): - cfg = Config() - self.assertNotIn('_repr_html_', cfg) - with self.assertRaises(AttributeError): - _ = cfg._repr_html_ - self.assertNotIn('_repr_html_', cfg) - self.assertEqual(len(cfg), 0) - - def test_getitem_not_section(self): - cfg = Config() - self.assertNotIn('foo', cfg) - foo = cfg['foo'] - assert isinstance(foo, LazyConfigValue) - self.assertIn('foo', cfg) - - def test_merge_no_copies(self): - c = Config() - c2 = Config() - c2.Foo.trait = [] - c.merge(c2) - c2.Foo.trait.append(1) - self.assertIs(c.Foo, c2.Foo) - self.assertEqual(c.Foo.trait, [1]) - self.assertEqual(c2.Foo.trait, [1]) - diff --git a/contrib/python/traitlets/py2/traitlets/log.py b/contrib/python/traitlets/py2/traitlets/log.py deleted file mode 100644 index af86b325f51..00000000000 --- a/contrib/python/traitlets/py2/traitlets/log.py +++ /dev/null @@ -1,27 +0,0 @@ -"""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. - - 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: - _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 diff --git a/contrib/python/traitlets/py2/traitlets/tests/__init__.py b/contrib/python/traitlets/py2/traitlets/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 --- a/contrib/python/traitlets/py2/traitlets/tests/__init__.py +++ /dev/null diff --git a/contrib/python/traitlets/py2/traitlets/tests/_warnings.py b/contrib/python/traitlets/py2/traitlets/tests/_warnings.py deleted file mode 100644 index f135d1f67ee..00000000000 --- a/contrib/python/traitlets/py2/traitlets/tests/_warnings.py +++ /dev/null @@ -1,107 +0,0 @@ -# From scikit-image: https://github.com/scikit-image/scikit-image/blob/c2f8c4ab123ebe5f7b827bc495625a32bb225c10/skimage/_shared/_warnings.py -# Licensed under modified BSD license - -__all__ = ['all_warnings', 'expected_warnings'] - -from contextlib import contextmanager -import sys -import warnings -import inspect -import re - - -@contextmanager -def all_warnings(): - """ - Context for use in testing to ensure that all warnings are raised. - Examples - -------- - >>> import warnings - >>> def foo(): - ... warnings.warn(RuntimeWarning("bar")) - We raise the warning once, while the warning filter is set to "once". - Hereafter, the warning is invisible, even with custom filters: - >>> with warnings.catch_warnings(): - ... warnings.simplefilter('once') - ... foo() - We can now run ``foo()`` without a warning being raised: - >>> from numpy.testing import assert_warns - >>> foo() - To catch the warning, we call in the help of ``all_warnings``: - >>> with all_warnings(): - ... assert_warns(RuntimeWarning, foo) - """ - - # Whenever a warning is triggered, Python adds a __warningregistry__ - # member to the *calling* module. The exercize here is to find - # and eradicate all those breadcrumbs that were left lying around. - # - # We proceed by first searching all parent calling frames and explicitly - # clearing their warning registries (necessary for the doctests above to - # pass). Then, we search for all submodules of skimage and clear theirs - # as well (necessary for the skimage test suite to pass). - - frame = inspect.currentframe() - if frame: - for f in inspect.getouterframes(frame): - f[0].f_locals['__warningregistry__'] = {} - del frame - - for mod_name, mod in list(sys.modules.items()): - if 'six.moves' in mod_name: - continue - try: - mod.__warningregistry__.clear() - except AttributeError: - pass - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - yield w - - -@contextmanager -def expected_warnings(matching): - """Context for use in testing to catch known warnings matching regexes - - Parameters - ---------- - matching : list of strings or compiled regexes - Regexes for the desired warning to catch - Examples - -------- - >>> from skimage import data, img_as_ubyte, img_as_float - >>> with expected_warnings(['precision loss']): - ... d = img_as_ubyte(img_as_float(data.coins())) - Notes - ----- - Uses `all_warnings` to ensure all warnings are raised. - Upon exiting, it checks the recorded warnings for the desired matching - pattern(s). - Raises a ValueError if any match was not found or an unexpected - warning was raised. - Allows for three types of behaviors: "and", "or", and "optional" matches. - This is done to accomodate different build enviroments or loop conditions - that may produce different warnings. The behaviors can be combined. - If you pass multiple patterns, you get an orderless "and", where all of the - warnings must be raised. - If you use the "|" operator in a pattern, you can catch one of several warnings. - Finally, you can use "|\A\Z" in a pattern to signify it as optional. - """ - with all_warnings() as w: - # enter context - yield w - # exited user context, check the recorded warnings - remaining = [m for m in matching if not '\A\Z' in m.split('|')] - for warn in w: - found = False - for match in matching: - if re.search(match, str(warn.message)) is not None: - found = True - if match in remaining: - remaining.remove(match) - if not found: - raise ValueError('Unexpected warning: %s' % str(warn.message)) - if len(remaining) > 0: - msg = 'No warning raised matching:\n%s' % '\n'.join(remaining) - raise ValueError(msg) diff --git a/contrib/python/traitlets/py2/traitlets/tests/test_traitlets.py b/contrib/python/traitlets/py2/traitlets/tests/test_traitlets.py deleted file mode 100644 index 11b334cb60a..00000000000 --- a/contrib/python/traitlets/py2/traitlets/tests/test_traitlets.py +++ /dev/null @@ -1,2419 +0,0 @@ -# encoding: utf-8 -"""Tests for traitlets.traitlets.""" - -# 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 pickle -import re -import sys -from ._warnings import expected_warnings - -from unittest import TestCase -import pytest -from pytest import mark - -from traitlets import ( - HasTraits, MetaHasTraits, TraitType, Any, Bool, CBytes, Dict, Enum, - Int, CInt, Long, CLong, Integer, Float, CFloat, Complex, Bytes, Unicode, - TraitError, Union, All, Undefined, Type, This, Instance, TCPAddress, - List, Tuple, ObjectName, DottedObjectName, CRegExp, link, directional_link, - ForwardDeclaredType, ForwardDeclaredInstance, validate, observe, default, - observe_compat, BaseDescriptor, HasDescriptors, -) - -import six - -def change_dict(*ordered_values): - change_names = ('name', 'old', 'new', 'owner', 'type') - return dict(zip(change_names, ordered_values)) - -#----------------------------------------------------------------------------- -# Helper classes for testing -#----------------------------------------------------------------------------- - - -class HasTraitsStub(HasTraits): - - def notify_change(self, change): - self._notify_name = change['name'] - self._notify_old = change['old'] - self._notify_new = change['new'] - self._notify_type = change['type'] - - -#----------------------------------------------------------------------------- -# Test classes -#----------------------------------------------------------------------------- - - -class TestTraitType(TestCase): - - def test_get_undefined(self): - class A(HasTraits): - a = TraitType - a = A() - with self.assertRaises(TraitError): - a.a - - def test_set(self): - class A(HasTraitsStub): - a = TraitType - - a = A() - a.a = 10 - self.assertEqual(a.a, 10) - self.assertEqual(a._notify_name, 'a') - self.assertEqual(a._notify_old, Undefined) - self.assertEqual(a._notify_new, 10) - - def test_validate(self): - class MyTT(TraitType): - def validate(self, inst, value): - return -1 - class A(HasTraitsStub): - tt = MyTT - - a = A() - a.tt = 10 - self.assertEqual(a.tt, -1) - - def test_default_validate(self): - class MyIntTT(TraitType): - def validate(self, obj, value): - if isinstance(value, int): - return value - self.error(obj, value) - class A(HasTraits): - tt = MyIntTT(10) - a = A() - self.assertEqual(a.tt, 10) - - # Defaults are validated when the HasTraits is instantiated - class B(HasTraits): - tt = MyIntTT('bad default') - self.assertRaises(TraitError, B) - - def test_info(self): - class A(HasTraits): - tt = TraitType - a = A() - self.assertEqual(A.tt.info(), 'any value') - - def test_error(self): - class A(HasTraits): - tt = TraitType - a = A() - self.assertRaises(TraitError, A.tt.error, a, 10) - - def test_deprecated_dynamic_initializer(self): - class A(HasTraits): - x = Int(10) - def _x_default(self): - return 11 - class B(A): - x = Int(20) - class C(A): - def _x_default(self): - return 21 - - a = A() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - b = B() - self.assertEqual(b.x, 20) - self.assertEqual(b._trait_values, {'x': 20}) - c = C() - self.assertEqual(c._trait_values, {}) - self.assertEqual(c.x, 21) - self.assertEqual(c._trait_values, {'x': 21}) - # Ensure that the base class remains unmolested when the _default - # initializer gets overridden in a subclass. - a = A() - c = C() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - - def test_dynamic_initializer(self): - - class A(HasTraits): - x = Int(10) - - @default('x') - def _default_x(self): - return 11 - - class B(A): - x = Int(20) - - class C(A): - - @default('x') - def _default_x(self): - return 21 - - a = A() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - b = B() - self.assertEqual(b.x, 20) - self.assertEqual(b._trait_values, {'x': 20}) - c = C() - self.assertEqual(c._trait_values, {}) - self.assertEqual(c.x, 21) - self.assertEqual(c._trait_values, {'x': 21}) - # Ensure that the base class remains unmolested when the _default - # initializer gets overridden in a subclass. - a = A() - c = C() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - - def test_tag_metadata(self): - class MyIntTT(TraitType): - metadata = {'a': 1, 'b': 2} - a = MyIntTT(10).tag(b=3, c=4) - self.assertEqual(a.metadata, {'a': 1, 'b': 3, 'c': 4}) - - def test_metadata_localized_instance(self): - class MyIntTT(TraitType): - metadata = {'a': 1, 'b': 2} - a = MyIntTT(10) - b = MyIntTT(10) - a.metadata['c'] = 3 - # make sure that changing a's metadata didn't change b's metadata - self.assertNotIn('c', b.metadata) - - def test_union_metadata(self): - class Foo(HasTraits): - bar = (Int().tag(ta=1) | Dict().tag(ta=2, ti='b')).tag(ti='a') - foo = Foo() - # At this point, no value has been set for bar, so value-specific - # is not set. - self.assertEqual(foo.trait_metadata('bar', 'ta'), None) - self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') - foo.bar = {} - self.assertEqual(foo.trait_metadata('bar', 'ta'), 2) - self.assertEqual(foo.trait_metadata('bar', 'ti'), 'b') - foo.bar = 1 - self.assertEqual(foo.trait_metadata('bar', 'ta'), 1) - self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') - - def test_union_default_value(self): - class Foo(HasTraits): - bar = Union([Dict(), Int()], default_value=1) - foo = Foo() - self.assertEqual(foo.bar, 1) - - def test_deprecated_metadata_access(self): - class MyIntTT(TraitType): - metadata = {'a': 1, 'b': 2} - a = MyIntTT(10) - with expected_warnings(["use the instance .metadata dictionary directly"]*2): - a.set_metadata('key', 'value') - v = a.get_metadata('key') - self.assertEqual(v, 'value') - with expected_warnings(["use the instance .help string directly"]*2): - a.set_metadata('help', 'some help') - v = a.get_metadata('help') - self.assertEqual(v, 'some help') - - def test_trait_types_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = Int - - def test_trait_types_list_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = List(Int) - - def test_trait_types_tuple_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = Tuple(Int) - - def test_trait_types_dict_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = Dict(Int) - -class TestHasDescriptorsMeta(TestCase): - - def test_metaclass(self): - self.assertEqual(type(HasTraits), MetaHasTraits) - - class A(HasTraits): - a = Int() - - a = A() - self.assertEqual(type(a.__class__), MetaHasTraits) - self.assertEqual(a.a,0) - a.a = 10 - self.assertEqual(a.a,10) - - class B(HasTraits): - b = Int() - - b = B() - self.assertEqual(b.b,0) - b.b = 10 - self.assertEqual(b.b,10) - - class C(HasTraits): - c = Int(30) - - c = C() - self.assertEqual(c.c,30) - c.c = 10 - self.assertEqual(c.c,10) - - def test_this_class(self): - class A(HasTraits): - t = This() - tt = This() - class B(A): - tt = This() - ttt = This() - self.assertEqual(A.t.this_class, A) - self.assertEqual(B.t.this_class, A) - self.assertEqual(B.tt.this_class, B) - self.assertEqual(B.ttt.this_class, B) - -class TestHasDescriptors(TestCase): - - def test_setup_instance(self): - - class FooDescriptor(BaseDescriptor): - - def instance_init(self, inst): - foo = inst.foo # instance should have the attr - - class HasFooDescriptors(HasDescriptors): - - fd = FooDescriptor() - - def setup_instance(self, *args, **kwargs): - self.foo = kwargs.get('foo', None) - super(HasFooDescriptors, self).setup_instance(*args, **kwargs) - - hfd = HasFooDescriptors(foo='bar') - -class TestHasTraitsNotify(TestCase): - - def setUp(self): - self._notify1 = [] - self._notify2 = [] - - def notify1(self, name, old, new): - self._notify1.append((name, old, new)) - - def notify2(self, name, old, new): - self._notify2.append((name, old, new)) - - def test_notify_all(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.on_trait_change(self.notify1) - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.b = 0.0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - self.assertTrue(('a',0,10) in self._notify1) - a.b = 10.0 - self.assertTrue(('b',0.0,10.0) in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - self.assertRaises(TraitError,setattr,a,'b','bad string') - self._notify1 = [] - a.on_trait_change(self.notify1,remove=True) - a.a = 20 - a.b = 20.0 - self.assertEqual(len(self._notify1),0) - - def test_notify_one(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.on_trait_change(self.notify1, 'a') - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - self.assertTrue(('a',0,10) in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - - def test_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - self.assertEqual(b.a,0) - self.assertEqual(b.b,0.0) - b.a = 100 - b.b = 100.0 - self.assertEqual(b.a,100) - self.assertEqual(b.b,100.0) - - def test_notify_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - b.on_trait_change(self.notify1, 'a') - b.on_trait_change(self.notify2, 'b') - b.a = 0 - b.b = 0.0 - self.assertEqual(len(self._notify1),0) - self.assertEqual(len(self._notify2),0) - b.a = 10 - b.b = 10.0 - self.assertTrue(('a',0,10) in self._notify1) - self.assertTrue(('b',0.0,10.0) in self._notify2) - - def test_static_notify(self): - - class A(HasTraits): - a = Int() - _notify1 = [] - def _a_changed(self, name, old, new): - self._notify1.append((name, old, new)) - - a = A() - a.a = 0 - # This is broken!!! - self.assertEqual(len(a._notify1),0) - a.a = 10 - self.assertTrue(('a',0,10) in a._notify1) - - class B(A): - b = Float() - _notify2 = [] - def _b_changed(self, name, old, new): - self._notify2.append((name, old, new)) - - b = B() - b.a = 10 - b.b = 10.0 - self.assertTrue(('a',0,10) in b._notify1) - self.assertTrue(('b',0.0,10.0) in b._notify2) - - def test_notify_args(self): - - def callback0(): - self.cb = () - def callback1(name): - self.cb = (name,) - def callback2(name, new): - self.cb = (name, new) - def callback3(name, old, new): - self.cb = (name, old, new) - def callback4(name, old, new, obj): - self.cb = (name, old, new, obj) - - class A(HasTraits): - a = Int() - - a = A() - a.on_trait_change(callback0, 'a') - a.a = 10 - self.assertEqual(self.cb,()) - a.on_trait_change(callback0, 'a', remove=True) - - a.on_trait_change(callback1, 'a') - a.a = 100 - self.assertEqual(self.cb,('a',)) - a.on_trait_change(callback1, 'a', remove=True) - - a.on_trait_change(callback2, 'a') - a.a = 1000 - self.assertEqual(self.cb,('a',1000)) - a.on_trait_change(callback2, 'a', remove=True) - - a.on_trait_change(callback3, 'a') - a.a = 10000 - self.assertEqual(self.cb,('a',1000,10000)) - a.on_trait_change(callback3, 'a', remove=True) - - a.on_trait_change(callback4, 'a') - a.a = 100000 - self.assertEqual(self.cb,('a',10000,100000,a)) - self.assertEqual(len(a._trait_notifiers['a']['change']), 1) - a.on_trait_change(callback4, 'a', remove=True) - - self.assertEqual(len(a._trait_notifiers['a']['change']), 0) - - def test_notify_only_once(self): - - class A(HasTraits): - listen_to = ['a'] - - a = Int(0) - b = 0 - - def __init__(self, **kwargs): - super(A, self).__init__(**kwargs) - self.on_trait_change(self.listener1, ['a']) - - def listener1(self, name, old, new): - self.b += 1 - - class B(A): - - c = 0 - d = 0 - - def __init__(self, **kwargs): - super(B, self).__init__(**kwargs) - self.on_trait_change(self.listener2) - - def listener2(self, name, old, new): - self.c += 1 - - def _a_changed(self, name, old, new): - self.d += 1 - - b = B() - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - -class TestObserveDecorator(TestCase): - - def setUp(self): - self._notify1 = [] - self._notify2 = [] - - def notify1(self, change): - self._notify1.append(change) - - def notify2(self, change): - self._notify2.append(change) - - def test_notify_all(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.observe(self.notify1) - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.b = 0.0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - change = change_dict('a', 0, 10, a, 'change') - self.assertTrue(change in self._notify1) - a.b = 10.0 - change = change_dict('b', 0.0, 10.0, a, 'change') - self.assertTrue(change in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - self.assertRaises(TraitError,setattr,a,'b','bad string') - self._notify1 = [] - a.unobserve(self.notify1) - a.a = 20 - a.b = 20.0 - self.assertEqual(len(self._notify1),0) - - def test_notify_one(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.observe(self.notify1, 'a') - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - change = change_dict('a', 0, 10, a, 'change') - self.assertTrue(change in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - - def test_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - self.assertEqual(b.a,0) - self.assertEqual(b.b,0.0) - b.a = 100 - b.b = 100.0 - self.assertEqual(b.a,100) - self.assertEqual(b.b,100.0) - - def test_notify_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - b.observe(self.notify1, 'a') - b.observe(self.notify2, 'b') - b.a = 0 - b.b = 0.0 - self.assertEqual(len(self._notify1),0) - self.assertEqual(len(self._notify2),0) - b.a = 10 - b.b = 10.0 - change = change_dict('a', 0, 10, b, 'change') - self.assertTrue(change in self._notify1) - change = change_dict('b', 0.0, 10.0, b, 'change') - self.assertTrue(change in self._notify2) - - def test_static_notify(self): - - class A(HasTraits): - a = Int() - b = Int() - _notify1 = [] - _notify_any = [] - - @observe('a') - def _a_changed(self, change): - self._notify1.append(change) - - @observe(All) - def _any_changed(self, change): - self._notify_any.append(change) - - a = A() - a.a = 0 - self.assertEqual(len(a._notify1),0) - a.a = 10 - change = change_dict('a', 0, 10, a, 'change') - self.assertTrue(change in a._notify1) - a.b = 1 - self.assertEqual(len(a._notify_any), 2) - change = change_dict('b', 0, 1, a, 'change') - self.assertTrue(change in a._notify_any) - - class B(A): - b = Float() - _notify2 = [] - @observe('b') - def _b_changed(self, change): - self._notify2.append(change) - - b = B() - b.a = 10 - b.b = 10.0 - change = change_dict('a', 0, 10, b, 'change') - self.assertTrue(change in b._notify1) - change = change_dict('b', 0.0, 10.0, b, 'change') - self.assertTrue(change in b._notify2) - - def test_notify_args(self): - - def callback0(): - self.cb = () - def callback1(change): - self.cb = change - - class A(HasTraits): - a = Int() - - a = A() - a.on_trait_change(callback0, 'a') - a.a = 10 - self.assertEqual(self.cb,()) - a.unobserve(callback0, 'a') - - a.observe(callback1, 'a') - a.a = 100 - change = change_dict('a', 10, 100, a, 'change') - self.assertEqual(self.cb, change) - self.assertEqual(len(a._trait_notifiers['a']['change']), 1) - a.unobserve(callback1, 'a') - - self.assertEqual(len(a._trait_notifiers['a']['change']), 0) - - def test_notify_only_once(self): - - class A(HasTraits): - listen_to = ['a'] - - a = Int(0) - b = 0 - - def __init__(self, **kwargs): - super(A, self).__init__(**kwargs) - self.observe(self.listener1, ['a']) - - def listener1(self, change): - self.b += 1 - - class B(A): - - c = 0 - d = 0 - - def __init__(self, **kwargs): - super(B, self).__init__(**kwargs) - self.observe(self.listener2) - - def listener2(self, change): - self.c += 1 - - @observe('a') - def _a_changed(self, change): - self.d += 1 - - b = B() - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - - -class TestHasTraits(TestCase): - - def test_trait_names(self): - class A(HasTraits): - i = Int() - f = Float() - a = A() - self.assertEqual(sorted(a.trait_names()),['f','i']) - self.assertEqual(sorted(A.class_trait_names()),['f','i']) - self.assertTrue(a.has_trait('f')) - self.assertFalse(a.has_trait('g')) - - def test_trait_metadata_deprecated(self): - with expected_warnings(['metadata should be set using the \.tag\(\) method']): - class A(HasTraits): - i = Int(config_key='MY_VALUE') - a = A() - self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') - - def test_trait_metadata(self): - class A(HasTraits): - i = Int().tag(config_key='MY_VALUE') - a = A() - self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') - - def test_trait_metadata_default(self): - class A(HasTraits): - i = Int() - a = A() - self.assertEqual(a.trait_metadata('i', 'config_key'), None) - self.assertEqual(a.trait_metadata('i', 'config_key', 'default'), 'default') - - def test_traits(self): - class A(HasTraits): - i = Int() - f = Float() - a = A() - self.assertEqual(a.traits(), dict(i=A.i, f=A.f)) - self.assertEqual(A.class_traits(), dict(i=A.i, f=A.f)) - - def test_traits_metadata(self): - class A(HasTraits): - i = Int().tag(config_key='VALUE1', other_thing='VALUE2') - f = Float().tag(config_key='VALUE3', other_thing='VALUE2') - j = Int(0) - a = A() - self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) - traits = a.traits(config_key='VALUE1', other_thing='VALUE2') - self.assertEqual(traits, dict(i=A.i)) - - # This passes, but it shouldn't because I am replicating a bug in - # traits. - traits = a.traits(config_key=lambda v: True) - self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) - - def test_traits_metadata_deprecated(self): - with expected_warnings(['metadata should be set using the \.tag\(\) method']*2): - class A(HasTraits): - i = Int(config_key='VALUE1', other_thing='VALUE2') - f = Float(config_key='VALUE3', other_thing='VALUE2') - j = Int(0) - a = A() - self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) - traits = a.traits(config_key='VALUE1', other_thing='VALUE2') - self.assertEqual(traits, dict(i=A.i)) - - # This passes, but it shouldn't because I am replicating a bug in - # traits. - traits = a.traits(config_key=lambda v: True) - self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) - - - def test_init(self): - class A(HasTraits): - i = Int() - x = Float() - a = A(i=1, x=10.0) - self.assertEqual(a.i, 1) - self.assertEqual(a.x, 10.0) - - def test_positional_args(self): - class A(HasTraits): - i = Int(0) - def __init__(self, i): - super(A, self).__init__() - self.i = i - - a = A(5) - self.assertEqual(a.i, 5) - # should raise TypeError if no positional arg given - self.assertRaises(TypeError, A) - -#----------------------------------------------------------------------------- -# Tests for specific trait types -#----------------------------------------------------------------------------- - - -class TestType(TestCase): - - def test_default(self): - - class B(object): pass - class A(HasTraits): - klass = Type(allow_none=True) - - a = A() - self.assertEqual(a.klass, object) - - a.klass = B - self.assertEqual(a.klass, B) - self.assertRaises(TraitError, setattr, a, 'klass', 10) - - def test_default_options(self): - - class B(object): pass - class C(B): pass - class A(HasTraits): - # Different possible combinations of options for default_value - # and klass. default_value=None is only valid with allow_none=True. - k1 = Type() - k2 = Type(None, allow_none=True) - k3 = Type(B) - k4 = Type(klass=B) - k5 = Type(default_value=None, klass=B, allow_none=True) - k6 = Type(default_value=C, klass=B) - - self.assertIs(A.k1.default_value, object) - self.assertIs(A.k1.klass, object) - self.assertIs(A.k2.default_value, None) - self.assertIs(A.k2.klass, object) - self.assertIs(A.k3.default_value, B) - self.assertIs(A.k3.klass, B) - self.assertIs(A.k4.default_value, B) - self.assertIs(A.k4.klass, B) - self.assertIs(A.k5.default_value, None) - self.assertIs(A.k5.klass, B) - self.assertIs(A.k6.default_value, C) - self.assertIs(A.k6.klass, B) - - a = A() - self.assertIs(a.k1, object) - self.assertIs(a.k2, None) - self.assertIs(a.k3, B) - self.assertIs(a.k4, B) - self.assertIs(a.k5, None) - self.assertIs(a.k6, C) - - def test_value(self): - - class B(object): pass - class C(object): pass - class A(HasTraits): - klass = Type(B) - - a = A() - self.assertEqual(a.klass, B) - self.assertRaises(TraitError, setattr, a, 'klass', C) - self.assertRaises(TraitError, setattr, a, 'klass', object) - a.klass = B - - def test_allow_none(self): - - class B(object): pass - class C(B): pass - class A(HasTraits): - klass = Type(B) - - a = A() - self.assertEqual(a.klass, B) - self.assertRaises(TraitError, setattr, a, 'klass', None) - a.klass = C - self.assertEqual(a.klass, C) - - def test_validate_klass(self): - - class A(HasTraits): - klass = Type('no strings allowed') - - self.assertRaises(ImportError, A) - - class A(HasTraits): - klass = Type('rub.adub.Duck') - - self.assertRaises(ImportError, A) - - def test_validate_default(self): - - class B(object): pass - class A(HasTraits): - klass = Type('bad default', B) - - self.assertRaises(ImportError, A) - - class C(HasTraits): - klass = Type(None, B) - - self.assertRaises(TraitError, C) - - def test_str_klass(self): - - class A(HasTraits): - klass = Type('ipython_genutils.ipstruct.Struct') - - from ipython_genutils.ipstruct import Struct - a = A() - a.klass = Struct - self.assertEqual(a.klass, Struct) - - self.assertRaises(TraitError, setattr, a, 'klass', 10) - - def test_set_str_klass(self): - - class A(HasTraits): - klass = Type() - - a = A(klass='ipython_genutils.ipstruct.Struct') - from ipython_genutils.ipstruct import Struct - self.assertEqual(a.klass, Struct) - -class TestInstance(TestCase): - - def test_basic(self): - class Foo(object): pass - class Bar(Foo): pass - class Bah(object): pass - - class A(HasTraits): - inst = Instance(Foo, allow_none=True) - - a = A() - self.assertTrue(a.inst is None) - a.inst = Foo() - self.assertTrue(isinstance(a.inst, Foo)) - a.inst = Bar() - self.assertTrue(isinstance(a.inst, Foo)) - self.assertRaises(TraitError, setattr, a, 'inst', Foo) - self.assertRaises(TraitError, setattr, a, 'inst', Bar) - self.assertRaises(TraitError, setattr, a, 'inst', Bah()) - - def test_default_klass(self): - class Foo(object): pass - class Bar(Foo): pass - class Bah(object): pass - - class FooInstance(Instance): - klass = Foo - - class A(HasTraits): - inst = FooInstance(allow_none=True) - - a = A() - self.assertTrue(a.inst is None) - a.inst = Foo() - self.assertTrue(isinstance(a.inst, Foo)) - a.inst = Bar() - self.assertTrue(isinstance(a.inst, Foo)) - self.assertRaises(TraitError, setattr, a, 'inst', Foo) - self.assertRaises(TraitError, setattr, a, 'inst', Bar) - self.assertRaises(TraitError, setattr, a, 'inst', Bah()) - - def test_unique_default_value(self): - class Foo(object): pass - class A(HasTraits): - inst = Instance(Foo,(),{}) - - a = A() - b = A() - self.assertTrue(a.inst is not b.inst) - - def test_args_kw(self): - class Foo(object): - def __init__(self, c): self.c = c - class Bar(object): pass - class Bah(object): - def __init__(self, c, d): - self.c = c; self.d = d - - class A(HasTraits): - inst = Instance(Foo, (10,)) - a = A() - self.assertEqual(a.inst.c, 10) - - class B(HasTraits): - inst = Instance(Bah, args=(10,), kw=dict(d=20)) - b = B() - self.assertEqual(b.inst.c, 10) - self.assertEqual(b.inst.d, 20) - - class C(HasTraits): - inst = Instance(Foo, allow_none=True) - c = C() - self.assertTrue(c.inst is None) - - def test_bad_default(self): - class Foo(object): pass - - class A(HasTraits): - inst = Instance(Foo) - - a = A() - with self.assertRaises(TraitError): - a.inst - - def test_instance(self): - class Foo(object): pass - - def inner(): - class A(HasTraits): - inst = Instance(Foo()) - - self.assertRaises(TraitError, inner) - - -class TestThis(TestCase): - - def test_this_class(self): - class Foo(HasTraits): - this = This() - - f = Foo() - self.assertEqual(f.this, None) - g = Foo() - f.this = g - self.assertEqual(f.this, g) - self.assertRaises(TraitError, setattr, f, 'this', 10) - - def test_this_inst(self): - class Foo(HasTraits): - this = This() - - f = Foo() - f.this = Foo() - self.assertTrue(isinstance(f.this, Foo)) - - def test_subclass(self): - class Foo(HasTraits): - t = This() - class Bar(Foo): - pass - f = Foo() - b = Bar() - f.t = b - b.t = f - self.assertEqual(f.t, b) - self.assertEqual(b.t, f) - - def test_subclass_override(self): - class Foo(HasTraits): - t = This() - class Bar(Foo): - t = This() - f = Foo() - b = Bar() - f.t = b - self.assertEqual(f.t, b) - self.assertRaises(TraitError, setattr, b, 't', f) - - def test_this_in_container(self): - - class Tree(HasTraits): - value = Unicode() - leaves = List(This()) - - tree = Tree( - value='foo', - leaves=[Tree(value='bar'), Tree(value='buzz')] - ) - - with self.assertRaises(TraitError): - tree.leaves = [1, 2] - -class TraitTestBase(TestCase): - """A best testing class for basic trait types.""" - - def assign(self, value): - self.obj.value = value - - def coerce(self, value): - return value - - def test_good_values(self): - if hasattr(self, '_good_values'): - for value in self._good_values: - self.assign(value) - self.assertEqual(self.obj.value, self.coerce(value)) - - def test_bad_values(self): - if hasattr(self, '_bad_values'): - for value in self._bad_values: - try: - self.assertRaises(TraitError, self.assign, value) - except AssertionError: - assert False, value - - def test_default_value(self): - if hasattr(self, '_default_value'): - self.assertEqual(self._default_value, self.obj.value) - - def test_allow_none(self): - if (hasattr(self, '_bad_values') and hasattr(self, '_good_values') and - None in self._bad_values): - trait=self.obj.traits()['value'] - try: - trait.allow_none = True - self._bad_values.remove(None) - #skip coerce. Allow None casts None to None. - self.assign(None) - self.assertEqual(self.obj.value,None) - self.test_good_values() - self.test_bad_values() - finally: - #tear down - trait.allow_none = False - self._bad_values.append(None) - - def tearDown(self): - # restore default value after tests, if set - if hasattr(self, '_default_value'): - self.obj.value = self._default_value - - -class AnyTrait(HasTraits): - - value = Any() - -class AnyTraitTest(TraitTestBase): - - obj = AnyTrait() - - _default_value = None - _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j] - _bad_values = [] - -class UnionTrait(HasTraits): - - value = Union([Type(), Bool()]) - -class UnionTraitTest(TraitTestBase): - - obj = UnionTrait(value='ipython_genutils.ipstruct.Struct') - _good_values = [int, float, True] - _bad_values = [[], (0,), 1j] - -class OrTrait(HasTraits): - - value = Bool() | Unicode() - -class OrTraitTest(TraitTestBase): - - obj = OrTrait() - _good_values = [True, False, 'ten'] - _bad_values = [[], (0,), 1j] - -class IntTrait(HasTraits): - - value = Int(99, min=-100) - -class TestInt(TraitTestBase): - - obj = IntTrait() - _default_value = 99 - _good_values = [10, -10] - _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, 1j, - 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L', - u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', -200] - if not six.PY3: - _bad_values.extend([long(10), long(-10), 10*sys.maxint, -10*sys.maxint]) - - -class CIntTrait(HasTraits): - value = CInt('5') - -class TestCInt(TraitTestBase): - obj = CIntTrait() - - _default_value = 5 - _good_values = ['10', '-10', u'10', u'-10', 10, 10.0, -10.0, 10.1] - _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), - None, 1j, '10.1', u'10.1'] - - def coerce(self, n): - return int(n) - - -class MinBoundCIntTrait(HasTraits): - value = CInt('5', min=3) - -class TestMinBoundCInt(TestCInt): - obj = MinBoundCIntTrait() - - _default_value = 5 - _good_values = [3, 3.0, '3'] - _bad_values = [2.6, 2, -3, -3.0] - - -class LongTrait(HasTraits): - - value = Long(99 if six.PY3 else long(99)) - -class TestLong(TraitTestBase): - - obj = LongTrait() - - _default_value = 99 if six.PY3 else long(99) - _good_values = [10, -10] - _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), - None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1', - '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1', - u'-10.1'] - if not six.PY3: - # maxint undefined on py3, because int == long - _good_values.extend([long(10), long(-10), 10*sys.maxint, -10*sys.maxint]) - _bad_values.extend([[long(10)], (long(10),)]) - - @mark.skipif(six.PY3, reason="not relevant on py3") - def test_cast_small(self): - """Long casts ints to long""" - self.obj.value = 10 - self.assertEqual(type(self.obj.value), long) - - -class MinBoundLongTrait(HasTraits): - value = Long(99 if six.PY3 else long(99), min=5) - -class TestMinBoundLong(TraitTestBase): - obj = MinBoundLongTrait() - - _default_value = 99 if six.PY3 else long(99) - _good_values = [5, 10] - _bad_values = [4, -10] - - -class MaxBoundLongTrait(HasTraits): - value = Long(5 if six.PY3 else long(5), max=10) - -class TestMaxBoundLong(TraitTestBase): - obj = MaxBoundLongTrait() - - _default_value = 5 if six.PY3 else long(5) - _good_values = [10, -2] - _bad_values = [11, 20] - - -class CLongTrait(HasTraits): - value = CLong('5') - -class TestCLong(TraitTestBase): - obj = CLongTrait() - - _default_value = 5 if six.PY3 else long(5) - _good_values = ['10', '-10', u'10', u'-10', 10, 10.0, -10.0, 10.1] - _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), - None, 1j, '10.1', u'10.1'] - - def coerce(self, n): - return int(n) if six.PY3 else long(n) - - -class MaxBoundCLongTrait(HasTraits): - value = CLong('5', max=10) - -class TestMaxBoundCLong(TestCLong): - obj = MaxBoundCLongTrait() - - _default_value = 5 if six.PY3 else long(5) - _good_values = [10, '10', 10.3] - _bad_values = [11.0, '11'] - - -class IntegerTrait(HasTraits): - value = Integer(1) - -class TestInteger(TestLong): - obj = IntegerTrait() - _default_value = 1 - - def coerce(self, n): - return int(n) - - @mark.skipif(six.PY3, reason="not relevant on py3") - def test_cast_small(self): - """Integer casts small longs to int""" - - self.obj.value = long(100) - self.assertEqual(type(self.obj.value), int) - - -class MinBoundIntegerTrait(HasTraits): - value = Integer(5, min=3) - -class TestMinBoundInteger(TraitTestBase): - obj = MinBoundIntegerTrait() - - _default_value = 5 - _good_values = 3, 20 - _bad_values = [2, -10] - - -class MaxBoundIntegerTrait(HasTraits): - value = Integer(1, max=3) - -class TestMaxBoundInteger(TraitTestBase): - obj = MaxBoundIntegerTrait() - - _default_value = 1 - _good_values = 3, -2 - _bad_values = [4, 10] - - -class FloatTrait(HasTraits): - - value = Float(99.0, max=200.0) - -class TestFloat(TraitTestBase): - - obj = FloatTrait() - - _default_value = 99.0 - _good_values = [10, -10, 10.1, -10.1] - _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, - 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10', - u'-10', u'10L', u'-10L', u'10.1', u'-10.1', 201.0] - if not six.PY3: - _bad_values.extend([long(10), long(-10)]) - - -class CFloatTrait(HasTraits): - - value = CFloat('99.0', max=200.0) - -class TestCFloat(TraitTestBase): - - obj = CFloatTrait() - - _default_value = 99.0 - _good_values = [10, 10.0, 10.5, '10.0', '10', '-10', '10.0', u'10'] - _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, 1j, - 200.1, '200.1'] - - def coerce(self, v): - return float(v) - - -class ComplexTrait(HasTraits): - - value = Complex(99.0-99.0j) - -class TestComplex(TraitTestBase): - - obj = ComplexTrait() - - _default_value = 99.0-99.0j - _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j, - 10.1j, 10.1+10.1j, 10.1-10.1j] - _bad_values = [u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None] - if not six.PY3: - _bad_values.extend([long(10), long(-10)]) - - -class BytesTrait(HasTraits): - - value = Bytes(b'string') - -class TestBytes(TraitTestBase): - - obj = BytesTrait() - - _default_value = b'string' - _good_values = [b'10', b'-10', b'10L', - b'-10L', b'10.1', b'-10.1', b'string'] - _bad_values = [10, -10, 10.1, -10.1, 1j, [10], - ['ten'],{'ten': 10},(10,), None, u'string'] - if not six.PY3: - _bad_values.extend([long(10), long(-10)]) - - -class UnicodeTrait(HasTraits): - - value = Unicode(u'unicode') - -class TestUnicode(TraitTestBase): - - obj = UnicodeTrait() - - _default_value = u'unicode' - _good_values = ['10', '-10', '10L', '-10L', '10.1', - '-10.1', '', u'', 'string', u'string', u"€"] - _bad_values = [10, -10, 10.1, -10.1, 1j, - [10], ['ten'], [u'ten'], {'ten': 10},(10,), None] - if not six.PY3: - _bad_values.extend([long(10), long(-10)]) - - -class ObjectNameTrait(HasTraits): - value = ObjectName("abc") - -class TestObjectName(TraitTestBase): - obj = ObjectNameTrait() - - _default_value = "abc" - _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"] - _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]", - None, object(), object] - if sys.version_info[0] < 3: - _bad_values.append(u"þ") - else: - _good_values.append(u"þ") # þ=1 is valid in Python 3 (PEP 3131). - - -class DottedObjectNameTrait(HasTraits): - value = DottedObjectName("a.b") - -class TestDottedObjectName(TraitTestBase): - obj = DottedObjectNameTrait() - - _default_value = "a.b" - _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"] - _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc.", None] - if sys.version_info[0] < 3: - _bad_values.append(u"t.þ") - else: - _good_values.append(u"t.þ") - - -class TCPAddressTrait(HasTraits): - value = TCPAddress() - -class TestTCPAddress(TraitTestBase): - - obj = TCPAddressTrait() - - _default_value = ('127.0.0.1',0) - _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)] - _bad_values = [(0,0),('localhost',10.0),('localhost',-1), None] - -class ListTrait(HasTraits): - - value = List(Int()) - -class TestList(TraitTestBase): - - obj = ListTrait() - - _default_value = [] - _good_values = [[], [1], list(range(10)), (1,2)] - _bad_values = [10, [1,'a'], 'a'] - - def coerce(self, value): - if value is not None: - value = list(value) - return value - -class Foo(object): - pass - -class NoneInstanceListTrait(HasTraits): - - value = List(Instance(Foo)) - -class TestNoneInstanceList(TraitTestBase): - - obj = NoneInstanceListTrait() - - _default_value = [] - _good_values = [[Foo(), Foo()], []] - _bad_values = [[None], [Foo(), None]] - - -class InstanceListTrait(HasTraits): - - value = List(Instance(__name__+'.Foo')) - -class TestInstanceList(TraitTestBase): - - obj = InstanceListTrait() - - def test_klass(self): - """Test that the instance klass is properly assigned.""" - self.assertIs(self.obj.traits()['value']._trait.klass, Foo) - - _default_value = [] - _good_values = [[Foo(), Foo()], []] - _bad_values = [['1', 2,], '1', [Foo], None] - -class UnionListTrait(HasTraits): - - value = List(Int() | Bool()) - -class TestUnionListTrait(HasTraits): - - obj = UnionListTrait() - - _default_value = [] - _good_values = [[True, 1], [False, True]] - _bad_values = [[1, 'True'], False] - - -class LenListTrait(HasTraits): - - value = List(Int(), [0], minlen=1, maxlen=2) - -class TestLenList(TraitTestBase): - - obj = LenListTrait() - - _default_value = [0] - _good_values = [[1], [1,2], (1,2)] - _bad_values = [10, [1,'a'], 'a', [], list(range(3))] - - def coerce(self, value): - if value is not None: - value = list(value) - return value - -class TupleTrait(HasTraits): - - value = Tuple(Int(allow_none=True), default_value=(1,)) - -class TestTupleTrait(TraitTestBase): - - obj = TupleTrait() - - _default_value = (1,) - _good_values = [(1,), (0,), [1]] - _bad_values = [10, (1, 2), ('a'), (), None] - - def coerce(self, value): - if value is not None: - value = tuple(value) - return value - - def test_invalid_args(self): - self.assertRaises(TypeError, Tuple, 5) - self.assertRaises(TypeError, Tuple, default_value='hello') - t = Tuple(Int(), CBytes(), default_value=(1,5)) - -class LooseTupleTrait(HasTraits): - - value = Tuple((1,2,3)) - -class TestLooseTupleTrait(TraitTestBase): - - obj = LooseTupleTrait() - - _default_value = (1,2,3) - _good_values = [(1,), [1], (0,), tuple(range(5)), tuple('hello'), ('a',5), ()] - _bad_values = [10, 'hello', {}, None] - - def coerce(self, value): - if value is not None: - value = tuple(value) - return value - - def test_invalid_args(self): - self.assertRaises(TypeError, Tuple, 5) - self.assertRaises(TypeError, Tuple, default_value='hello') - t = Tuple(Int(), CBytes(), default_value=(1,5)) - - -class MultiTupleTrait(HasTraits): - - value = Tuple(Int(), Bytes(), default_value=[99,b'bottles']) - -class TestMultiTuple(TraitTestBase): - - obj = MultiTupleTrait() - - _default_value = (99,b'bottles') - _good_values = [(1,b'a'), (2,b'b')] - _bad_values = ((),10, b'a', (1,b'a',3), (b'a',1), (1, u'a')) - -class CRegExpTrait(HasTraits): - - value = CRegExp(r'') - -class TestCRegExp(TraitTestBase): - - def coerce(self, value): - return re.compile(value) - - obj = CRegExpTrait() - - _default_value = re.compile(r'') - _good_values = [r'\d+', re.compile(r'\d+')] - _bad_values = ['(', None, ()] - -class DictTrait(HasTraits): - value = Dict() - -def test_dict_assignment(): - d = dict() - c = DictTrait() - c.value = d - d['a'] = 5 - assert d == c.value - assert c.value is d - - -class UniformlyValidatedDictTrait(HasTraits): - - value = Dict(trait=Unicode(), - default_value={'foo': '1'}) - - -class TestInstanceUniformlyValidatedDict(TraitTestBase): - - obj = UniformlyValidatedDictTrait() - - _default_value = {'foo': '1'} - _good_values = [{'foo': '0', 'bar': '1'}] - _bad_values = [{'foo': 0, 'bar': '1'}] - - -class KeyValidatedDictTrait(HasTraits): - - value = Dict(traits={'foo': Int()}, - default_value={'foo': 1}) - - -class TestInstanceKeyValidatedDict(TraitTestBase): - - obj = KeyValidatedDictTrait() - - _default_value = {'foo': 1} - _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 0, 'bar': 1}] - _bad_values = [{'foo': '0', 'bar': '1'}] - - -class FullyValidatedDictTrait(HasTraits): - - value = Dict(trait=Unicode(), - traits={'foo': Int()}, - default_value={'foo': 1}) - - -class TestInstanceFullyValidatedDict(TraitTestBase): - - obj = FullyValidatedDictTrait() - - _default_value = {'foo': 1} - _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 1, 'bar': '2'}] - _bad_values = [{'foo': 0, 'bar': 1}, {'foo': '0', 'bar': '1'}] - - -def test_dict_default_value(): - """Check that the `{}` default value of the Dict traitlet constructor is - actually copied.""" - - class Foo(HasTraits): - d1 = Dict() - d2 = Dict() - - foo = Foo() - assert foo.d1 == {} - assert foo.d2 == {} - assert foo.d1 is not foo.d2 - - -class TestValidationHook(TestCase): - - def test_parity_trait(self): - """Verify that the early validation hook is effective""" - - class Parity(HasTraits): - - value = Int(0) - parity = Enum(['odd', 'even'], default_value='even') - - @validate('value') - def _value_validate(self, proposal): - value = proposal['value'] - if self.parity == 'even' and value % 2: - raise TraitError('Expected an even number') - if self.parity == 'odd' and (value % 2 == 0): - raise TraitError('Expected an odd number') - return value - - u = Parity() - u.parity = 'odd' - u.value = 1 # OK - with self.assertRaises(TraitError): - u.value = 2 # Trait Error - - u.parity = 'even' - u.value = 2 # OK - - def test_multiple_validate(self): - """Verify that we can register the same validator to multiple names""" - - class OddEven(HasTraits): - - odd = Int(1) - even = Int(0) - - @validate('odd', 'even') - def check_valid(self, proposal): - if proposal['trait'].name == 'odd' and not proposal['value'] % 2: - raise TraitError('odd should be odd') - if proposal['trait'].name == 'even' and proposal['value'] % 2: - raise TraitError('even should be even') - - u = OddEven() - u.odd = 3 # OK - with self.assertRaises(TraitError): - u.odd = 2 # Trait Error - - u.even = 2 # OK - with self.assertRaises(TraitError): - u.even = 3 # Trait Error - - - -class TestLink(TestCase): - - def test_connect_same(self): - """Verify two traitlets of the same type can be linked together using link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Conenct the two classes. - c = link((a, 'value'), (b, 'value')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.value) - - # Change one of the values to make sure they stay in sync. - a.value = 5 - self.assertEqual(a.value, b.value) - b.value = 6 - self.assertEqual(a.value, b.value) - - def test_link_different(self): - """Verify two traitlets of different types can be linked together using link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - class B(HasTraits): - count = Int() - a = A(value=9) - b = B(count=8) - - # Conenct the two classes. - c = link((a, 'value'), (b, 'count')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.count) - - # Change one of the values to make sure they stay in sync. - a.value = 5 - self.assertEqual(a.value, b.count) - b.count = 4 - self.assertEqual(a.value, b.count) - - def test_unlink(self): - """Verify two linked traitlets can be unlinked.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Connect the two classes. - c = link((a, 'value'), (b, 'value')) - a.value = 4 - c.unlink() - - # Change one of the values to make sure they don't stay in sync. - a.value = 5 - self.assertNotEqual(a.value, b.value) - - def test_callbacks(self): - """Verify two linked traitlets have their callbacks called once.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - class B(HasTraits): - count = Int() - a = A(value=9) - b = B(count=8) - - # Register callbacks that count. - callback_count = [] - def a_callback(name, old, new): - callback_count.append('a') - a.on_trait_change(a_callback, 'value') - def b_callback(name, old, new): - callback_count.append('b') - b.on_trait_change(b_callback, 'count') - - # Connect the two classes. - c = link((a, 'value'), (b, 'count')) - - # Make sure b's count was set to a's value once. - self.assertEqual(''.join(callback_count), 'b') - del callback_count[:] - - # Make sure a's value was set to b's count once. - b.count = 5 - self.assertEqual(''.join(callback_count), 'ba') - del callback_count[:] - - # Make sure b's count was set to a's value once. - a.value = 4 - self.assertEqual(''.join(callback_count), 'ab') - del callback_count[:] - -class TestDirectionalLink(TestCase): - def test_connect_same(self): - """Verify two traitlets of the same type can be linked together using directional_link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Conenct the two classes. - c = directional_link((a, 'value'), (b, 'value')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.value) - - # Change one the value of the source and check that it synchronizes the target. - a.value = 5 - self.assertEqual(b.value, 5) - # Change one the value of the target and check that it has no impact on the source - b.value = 6 - self.assertEqual(a.value, 5) - - def test_tranform(self): - """Test transform link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Conenct the two classes. - c = directional_link((a, 'value'), (b, 'value'), lambda x: 2 * x) - - # Make sure the values are correct at the point of linking. - self.assertEqual(b.value, 2 * a.value) - - # Change one the value of the source and check that it modifies the target. - a.value = 5 - self.assertEqual(b.value, 10) - # Change one the value of the target and check that it has no impact on the source - b.value = 6 - self.assertEqual(a.value, 5) - - def test_link_different(self): - """Verify two traitlets of different types can be linked together using link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - class B(HasTraits): - count = Int() - a = A(value=9) - b = B(count=8) - - # Conenct the two classes. - c = directional_link((a, 'value'), (b, 'count')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.count) - - # Change one the value of the source and check that it synchronizes the target. - a.value = 5 - self.assertEqual(b.count, 5) - # Change one the value of the target and check that it has no impact on the source - b.value = 6 - self.assertEqual(a.value, 5) - - def test_unlink(self): - """Verify two linked traitlets can be unlinked.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Connect the two classes. - c = directional_link((a, 'value'), (b, 'value')) - a.value = 4 - c.unlink() - - # Change one of the values to make sure they don't stay in sync. - a.value = 5 - self.assertNotEqual(a.value, b.value) - -class Pickleable(HasTraits): - - i = Int() - @observe('i') - def _i_changed(self, change): pass - @validate('i') - def _i_validate(self, commit): - return commit['value'] - - j = Int() - - def __init__(self): - with self.hold_trait_notifications(): - self.i = 1 - self.on_trait_change(self._i_changed, 'i') - -def test_pickle_hastraits(): - c = Pickleable() - for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - p = pickle.dumps(c, protocol) - c2 = pickle.loads(p) - assert c2.i == c.i - assert c2.j == c.j - - c.i = 5 - for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - p = pickle.dumps(c, protocol) - c2 = pickle.loads(p) - assert c2.i == c.i - assert c2.j == c.j - - -def test_hold_trait_notifications(): - changes = [] - - class Test(HasTraits): - a = Integer(0) - b = Integer(0) - - def _a_changed(self, name, old, new): - changes.append((old, new)) - - def _b_validate(self, value, trait): - if value != 0: - raise TraitError('Only 0 is a valid value') - return value - - # Test context manager and nesting - t = Test() - with t.hold_trait_notifications(): - with t.hold_trait_notifications(): - t.a = 1 - assert t.a == 1 - assert changes == [] - t.a = 2 - assert t.a == 2 - with t.hold_trait_notifications(): - t.a = 3 - assert t.a == 3 - assert changes == [] - t.a = 4 - assert t.a == 4 - assert changes == [] - t.a = 4 - assert t.a == 4 - assert changes == [] - - assert changes == [(0, 4)] - # Test roll-back - try: - with t.hold_trait_notifications(): - t.b = 1 # raises a Trait error - except: - pass - assert t.b == 0 - - -class RollBack(HasTraits): - bar = Int() - def _bar_validate(self, value, trait): - if value: - raise TraitError('foobar') - return value - - -class TestRollback(TestCase): - - def test_roll_back(self): - - def assign_rollback(): - RollBack(bar=1) - - self.assertRaises(TraitError, assign_rollback) - - -class CacheModification(HasTraits): - foo = Int() - bar = Int() - - def _bar_validate(self, value, trait): - self.foo = value - return value - - def _foo_validate(self, value, trait): - self.bar = value - return value - - -def test_cache_modification(): - CacheModification(foo=1) - CacheModification(bar=1) - - -class OrderTraits(HasTraits): - notified = Dict() - - a = Unicode() - b = Unicode() - c = Unicode() - d = Unicode() - e = Unicode() - f = Unicode() - g = Unicode() - h = Unicode() - i = Unicode() - j = Unicode() - k = Unicode() - l = Unicode() - - def _notify(self, name, old, new): - """check the value of all traits when each trait change is triggered - - This verifies that the values are not sensitive - to dict ordering when loaded from kwargs - """ - # check the value of the other traits - # when a given trait change notification fires - self.notified[name] = { - c: getattr(self, c) for c in 'abcdefghijkl' - } - - def __init__(self, **kwargs): - self.on_trait_change(self._notify) - super(OrderTraits, self).__init__(**kwargs) - -def test_notification_order(): - d = {c:c for c in 'abcdefghijkl'} - obj = OrderTraits() - assert obj.notified == {} - obj = OrderTraits(**d) - notifications = { - c: d for c in 'abcdefghijkl' - } - assert obj.notified == notifications - - - -### -# Traits for Forward Declaration Tests -### -class ForwardDeclaredInstanceTrait(HasTraits): - - value = ForwardDeclaredInstance('ForwardDeclaredBar', allow_none=True) - -class ForwardDeclaredTypeTrait(HasTraits): - - value = ForwardDeclaredType('ForwardDeclaredBar', allow_none=True) - -class ForwardDeclaredInstanceListTrait(HasTraits): - - value = List(ForwardDeclaredInstance('ForwardDeclaredBar')) - -class ForwardDeclaredTypeListTrait(HasTraits): - - value = List(ForwardDeclaredType('ForwardDeclaredBar')) -### -# End Traits for Forward Declaration Tests -### - -### -# Classes for Forward Declaration Tests -### -class ForwardDeclaredBar(object): - pass - -class ForwardDeclaredBarSub(ForwardDeclaredBar): - pass -### -# End Classes for Forward Declaration Tests -### - -### -# Forward Declaration Tests -### -class TestForwardDeclaredInstanceTrait(TraitTestBase): - - obj = ForwardDeclaredInstanceTrait() - _default_value = None - _good_values = [None, ForwardDeclaredBar(), ForwardDeclaredBarSub()] - _bad_values = ['foo', 3, ForwardDeclaredBar, ForwardDeclaredBarSub] - -class TestForwardDeclaredTypeTrait(TraitTestBase): - - obj = ForwardDeclaredTypeTrait() - _default_value = None - _good_values = [None, ForwardDeclaredBar, ForwardDeclaredBarSub] - _bad_values = ['foo', 3, ForwardDeclaredBar(), ForwardDeclaredBarSub()] - -class TestForwardDeclaredInstanceList(TraitTestBase): - - obj = ForwardDeclaredInstanceListTrait() - - def test_klass(self): - """Test that the instance klass is properly assigned.""" - self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) - - _default_value = [] - _good_values = [ - [ForwardDeclaredBar(), ForwardDeclaredBarSub()], - [], - ] - _bad_values = [ - ForwardDeclaredBar(), - [ForwardDeclaredBar(), 3, None], - '1', - # Note that this is the type, not an instance. - [ForwardDeclaredBar], - [None], - None, - ] - -class TestForwardDeclaredTypeList(TraitTestBase): - - obj = ForwardDeclaredTypeListTrait() - - def test_klass(self): - """Test that the instance klass is properly assigned.""" - self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) - - _default_value = [] - _good_values = [ - [ForwardDeclaredBar, ForwardDeclaredBarSub], - [], - ] - _bad_values = [ - ForwardDeclaredBar, - [ForwardDeclaredBar, 3], - '1', - # Note that this is an instance, not the type. - [ForwardDeclaredBar()], - [None], - None, - ] -### -# End Forward Declaration Tests -### - -class TestDynamicTraits(TestCase): - - def setUp(self): - self._notify1 = [] - - def notify1(self, name, old, new): - self._notify1.append((name, old, new)) - - def test_notify_all(self): - - class A(HasTraits): - pass - - a = A() - self.assertTrue(not hasattr(a, 'x')) - self.assertTrue(not hasattr(a, 'y')) - - # Dynamically add trait x. - a.add_traits(x=Int()) - self.assertTrue(hasattr(a, 'x')) - self.assertTrue(isinstance(a, (A, ))) - - # Dynamically add trait y. - a.add_traits(y=Float()) - self.assertTrue(hasattr(a, 'y')) - self.assertTrue(isinstance(a, (A, ))) - self.assertEqual(a.__class__.__name__, A.__name__) - - # Create a new instance and verify that x and y - # aren't defined. - b = A() - self.assertTrue(not hasattr(b, 'x')) - self.assertTrue(not hasattr(b, 'y')) - - # Verify that notification works like normal. - a.on_trait_change(self.notify1) - a.x = 0 - self.assertEqual(len(self._notify1), 0) - a.y = 0.0 - self.assertEqual(len(self._notify1), 0) - a.x = 10 - self.assertTrue(('x', 0, 10) in self._notify1) - a.y = 10.0 - self.assertTrue(('y', 0.0, 10.0) in self._notify1) - self.assertRaises(TraitError, setattr, a, 'x', 'bad string') - self.assertRaises(TraitError, setattr, a, 'y', 'bad string') - self._notify1 = [] - a.on_trait_change(self.notify1, remove=True) - a.x = 20 - a.y = 20.0 - self.assertEqual(len(self._notify1), 0) - - -def test_enum_no_default(): - class C(HasTraits): - t = Enum(['a', 'b']) - - c = C() - c.t = 'a' - assert c.t == 'a' - - c = C() - - with pytest.raises(TraitError): - t = c.t - - c = C(t='b') - assert c.t == 'b' - - -def test_default_value_repr(): - class C(HasTraits): - t = Type('traitlets.HasTraits') - t2 = Type(HasTraits) - n = Integer(0) - lis = List() - d = Dict() - - assert C.t.default_value_repr() == "'traitlets.HasTraits'" - assert C.t2.default_value_repr() == "'traitlets.traitlets.HasTraits'" - assert C.n.default_value_repr() == '0' - assert C.lis.default_value_repr() == '[]' - assert C.d.default_value_repr() == '{}' - - -class TransitionalClass(HasTraits): - - d = Any() - @default('d') - def _d_default(self): - return TransitionalClass - - parent_super = False - calls_super = Integer(0) - - @default('calls_super') - def _calls_super_default(self): - return -1 - - @observe('calls_super') - @observe_compat - def _calls_super_changed(self, change): - self.parent_super = change - - parent_override = False - overrides = Integer(0) - - @observe('overrides') - @observe_compat - def _overrides_changed(self, change): - self.parent_override = change - - -class SubClass(TransitionalClass): - def _d_default(self): - return SubClass - - subclass_super = False - def _calls_super_changed(self, name, old, new): - self.subclass_super = True - super(SubClass, self)._calls_super_changed(name, old, new) - - subclass_override = False - def _overrides_changed(self, name, old, new): - self.subclass_override = True - - -def test_subclass_compat(): - obj = SubClass() - obj.calls_super = 5 - assert obj.parent_super - assert obj.subclass_super - obj.overrides = 5 - assert obj.subclass_override - assert not obj.parent_override - assert obj.d is SubClass - - -class DefinesHandler(HasTraits): - parent_called = False - - trait = Integer() - @observe('trait') - def handler(self, change): - self.parent_called = True - - -class OverridesHandler(DefinesHandler): - child_called = False - - @observe('trait') - def handler(self, change): - self.child_called = True - - -def test_subclass_override_observer(): - obj = OverridesHandler() - obj.trait = 5 - assert obj.child_called - assert not obj.parent_called - - -class DoesntRegisterHandler(DefinesHandler): - child_called = False - - def handler(self, change): - self.child_called = True - - -def test_subclass_override_not_registered(): - """Subclass that overrides observer and doesn't re-register unregisters both""" - obj = DoesntRegisterHandler() - obj.trait = 5 - assert not obj.child_called - assert not obj.parent_called - - -class AddsHandler(DefinesHandler): - child_called = False - - @observe('trait') - def child_handler(self, change): - self.child_called = True - -def test_subclass_add_observer(): - obj = AddsHandler() - obj.trait = 5 - assert obj.child_called - assert obj.parent_called - - -def test_observe_iterables(): - - class C(HasTraits): - i = Integer() - s = Unicode() - - c = C() - recorded = {} - def record(change): - recorded['change'] = change - - # observe with names=set - c.observe(record, names={'i', 's'}) - c.i = 5 - assert recorded['change'].name == 'i' - assert recorded['change'].new == 5 - c.s = 'hi' - assert recorded['change'].name == 's' - assert recorded['change'].new == 'hi' - - # observe with names=custom container with iter, contains - class MyContainer(object): - def __init__(self, container): - self.container = container - - def __iter__(self): - return iter(self.container) - - def __contains__(self, key): - return key in self.container - - c.observe(record, names=MyContainer({'i', 's'})) - c.i = 10 - assert recorded['change'].name == 'i' - assert recorded['change'].new == 10 - c.s = 'ok' - assert recorded['change'].name == 's' - assert recorded['change'].new == 'ok' - - -def test_super_args(): - class SuperRecorder(object): - def __init__(self, *args, **kwargs): - self.super_args = args - self.super_kwargs = kwargs - - class SuperHasTraits(HasTraits, SuperRecorder): - i = Integer() - - obj = SuperHasTraits('a1', 'a2', b=10, i=5, c='x') - assert obj.i == 5 - assert not hasattr(obj, 'b') - assert not hasattr(obj, 'c') - assert obj.super_args == ('a1' , 'a2') - assert obj.super_kwargs == {'b': 10 , 'c': 'x'} - -def test_super_bad_args(): - class SuperHasTraits(HasTraits): - a = Integer() - - if sys.version_info < (3,): - # Legacy Python, object.__init__ warns itself, instead of raising - w = ['object.__init__'] - else: - w = ["Passing unrecoginized arguments"] - with expected_warnings(w): - obj = SuperHasTraits(a=1, b=2) - assert obj.a == 1 - assert not hasattr(obj, 'b') diff --git a/contrib/python/traitlets/py2/traitlets/tests/test_traitlets_enum.py b/contrib/python/traitlets/py2/traitlets/tests/test_traitlets_enum.py deleted file mode 100644 index 82259ae6c51..00000000000 --- a/contrib/python/traitlets/py2/traitlets/tests/test_traitlets_enum.py +++ /dev/null @@ -1,181 +0,0 @@ -# -*- coding: UTF-8 -*- -# pylint: disable=missing-docstring, too-few-public-methods -""" -Test the trait-type ``UseEnum``. -""" - -import unittest -import enum -from ipython_genutils.py3compat import string_types -from traitlets import HasTraits, TraitError, UseEnum - - -# ----------------------------------------------------------------------------- -# TEST SUPPORT: -# ----------------------------------------------------------------------------- -class Color(enum.Enum): - red = 1 - green = 2 - blue = 3 - yellow = 4 - -class OtherColor(enum.Enum): - red = 0 - green = 1 - - -# ----------------------------------------------------------------------------- -# TESTSUITE: -# ----------------------------------------------------------------------------- -class TestUseEnum(unittest.TestCase): - # pylint: disable=invalid-name - - class Example(HasTraits): - color = UseEnum(Color, help="Color enum") - - def test_assign_enum_value(self): - example = self.Example() - example.color = Color.green - self.assertEqual(example.color, Color.green) - - def test_assign_all_enum_values(self): - # pylint: disable=no-member - enum_values = [value for value in Color.__members__.values()] - for value in enum_values: - self.assertIsInstance(value, Color) - example = self.Example() - example.color = value - self.assertEqual(example.color, value) - self.assertIsInstance(value, Color) - - def test_assign_enum_value__with_other_enum_raises_error(self): - example = self.Example() - with self.assertRaises(TraitError): - example.color = OtherColor.green - - def test_assign_enum_name_1(self): - # -- CONVERT: string => Enum value (item) - example = self.Example() - example.color = "red" - self.assertEqual(example.color, Color.red) - - def test_assign_enum_value_name(self): - # -- CONVERT: string => Enum value (item) - # pylint: disable=no-member - enum_names = [enum_val.name for enum_val in Color.__members__.values()] - for value in enum_names: - self.assertIsInstance(value, string_types) - example = self.Example() - enum_value = Color.__members__.get(value) - example.color = value - self.assertIs(example.color, enum_value) - self.assertEqual(example.color.name, value) - - def test_assign_scoped_enum_value_name(self): - # -- CONVERT: string => Enum value (item) - scoped_names = ["Color.red", "Color.green", "Color.blue", "Color.yellow"] - for value in scoped_names: - example = self.Example() - example.color = value - self.assertIsInstance(example.color, Color) - self.assertEqual(str(example.color), value) - - def test_assign_bad_enum_value_name__raises_error(self): - # -- CONVERT: string => Enum value (item) - bad_enum_names = ["UNKNOWN_COLOR", "RED", "Green", "blue2"] - for value in bad_enum_names: - example = self.Example() - with self.assertRaises(TraitError): - example.color = value - - def test_assign_enum_value_number_1(self): - # -- CONVERT: number => Enum value (item) - example = self.Example() - example.color = 1 # == Color.red.value - example.color = Color.red.value - self.assertEqual(example.color, Color.red) - - def test_assign_enum_value_number(self): - # -- CONVERT: number => Enum value (item) - # pylint: disable=no-member - enum_numbers = [enum_val.value - for enum_val in Color.__members__.values()] - for value in enum_numbers: - self.assertIsInstance(value, int) - example = self.Example() - example.color = value - self.assertIsInstance(example.color, Color) - self.assertEqual(example.color.value, value) - - def test_assign_bad_enum_value_number__raises_error(self): - # -- CONVERT: number => Enum value (item) - bad_numbers = [-1, 0, 5] - for value in bad_numbers: - self.assertIsInstance(value, int) - assert UseEnum(Color).select_by_number(value, None) is None - example = self.Example() - with self.assertRaises(TraitError): - example.color = value - - def test_ctor_without_default_value(self): - # -- IMPLICIT: default_value = Color.red (first enum-value) - class Example2(HasTraits): - color = UseEnum(Color) - - example = Example2() - self.assertEqual(example.color, Color.red) - - def test_ctor_with_default_value_as_enum_value(self): - # -- CONVERT: number => Enum value (item) - class Example2(HasTraits): - color = UseEnum(Color, default_value=Color.green) - - example = Example2() - self.assertEqual(example.color, Color.green) - - - def test_ctor_with_default_value_none_and_not_allow_none(self): - # -- IMPLICIT: default_value = Color.red (first enum-value) - class Example2(HasTraits): - color1 = UseEnum(Color, default_value=None, allow_none=False) - color2 = UseEnum(Color, default_value=None) - example = Example2() - self.assertEqual(example.color1, Color.red) - self.assertEqual(example.color2, Color.red) - - def test_ctor_with_default_value_none_and_allow_none(self): - class Example2(HasTraits): - color1 = UseEnum(Color, default_value=None, allow_none=True) - color2 = UseEnum(Color, allow_none=True) - - example = Example2() - self.assertIs(example.color1, None) - self.assertIs(example.color2, None) - - def test_assign_none_without_allow_none_resets_to_default_value(self): - class Example2(HasTraits): - color1 = UseEnum(Color, allow_none=False) - color2 = UseEnum(Color) - - example = Example2() - example.color1 = None - example.color2 = None - self.assertIs(example.color1, Color.red) - self.assertIs(example.color2, Color.red) - - def test_assign_none_to_enum_or_none(self): - class Example2(HasTraits): - color = UseEnum(Color, allow_none=True) - - example = Example2() - example.color = None - self.assertIs(example.color, None) - - def test_assign_bad_value_with_to_enum_or_none(self): - class Example2(HasTraits): - color = UseEnum(Color, allow_none=True) - - example = Example2() - with self.assertRaises(TraitError): - example.color = "BAD_VALUE" - diff --git a/contrib/python/traitlets/py2/traitlets/tests/utils.py b/contrib/python/traitlets/py2/traitlets/tests/utils.py deleted file mode 100644 index 88845d8519d..00000000000 --- a/contrib/python/traitlets/py2/traitlets/tests/utils.py +++ /dev/null @@ -1,39 +0,0 @@ -import sys - -from subprocess import Popen, PIPE - -def get_output_error_code(cmd): - """Get stdout, stderr, and exit code from running a command""" - p = Popen(cmd, stdout=PIPE, stderr=PIPE) - out, err = p.communicate() - out = out.decode('utf8', 'replace') - err = err.decode('utf8', 'replace') - return out, err, p.returncode - - -def check_help_output(pkg, subcommand=None): - """test that `python -m PKG [subcommand] -h` works""" - cmd = [sys.executable, '-m', pkg] - if subcommand: - cmd.extend(subcommand) - cmd.append('-h') - out, err, rc = get_output_error_code(cmd) - assert rc == 0, err - assert "Traceback" not in err - assert "Options" in out - assert "--help-all" in out - return out, err - - -def check_help_all_output(pkg, subcommand=None): - """test that `python -m PKG --help-all` works""" - cmd = [sys.executable, '-m', pkg] - if subcommand: - cmd.extend(subcommand) - cmd.append('--help-all') - out, err, rc = get_output_error_code(cmd) - assert rc == 0, err - assert "Traceback" not in err - assert "Options" in out - assert "Class parameters" in out - return out, err diff --git a/contrib/python/traitlets/py2/traitlets/traitlets.py b/contrib/python/traitlets/py2/traitlets/traitlets.py deleted file mode 100644 index c07daf7400d..00000000000 --- a/contrib/python/traitlets/py2/traitlets/traitlets.py +++ /dev/null @@ -1,2690 +0,0 @@ -# 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 enum -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.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 -#----------------------------------------------------------------------------- - -from ipython_genutils.py3compat import cast_unicode_py2 - -_name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$") - -def isidentifier(s): - if six.PY2: - return bool(_name_re.match(s)) - else: - return s.isidentifier() - -_deprecations_shown = set() -def _should_warn(key): - """Add our own checks for too many deprecation warnings. - - Limit to once per package. - """ - env_flag = os.environ.get('TRAITLETS_ALL_DEPRECATIONS') - if env_flag and env_flag != '0': - return True - - if key not in _deprecations_shown: - _deprecations_shown.add(key) - return True - 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. - """ - 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 - # 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 - 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'). - """ - 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) - 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] - """ - if names is All or isinstance(names, six.string_types): - return [names] - else: - 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(): - setattr(self.target[0], self.target[1], change.new) - - 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], - 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 - - -#----------------------------------------------------------------------------- -# 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' - - 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 - 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 '' - - 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 - mod = f.f_globals.get('__name__') or '' - pkg = mod.split('.', 1)[0] - key = tuple(['metadata-tag', pkg] + sorted(kwargs)) - if _should_warn(key): - warn("metadata %s was set from the constructor. " - "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() - self.metadata.update(kwargs) - else: - self.metadata = kwargs - 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 - """ - 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. - """ - 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: - - * 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. - 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: - 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)" - 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" - 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) - """ - 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.cb(change.name) - elif self.nargs == 2: - self.cb(change.name, change.new) - elif self.nargs == 3: - self.cb(change.name, change.old, change.new) - 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.""" - 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): - 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. - """ - 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. - - 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. - 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__ - warn("A parent of %s._%s_changed has adopted the new (traitlets 4.1) @observe(change) API" % ( - clsname, change_or_name), DeprecationWarning) - change = Bunch( - type='change', - old=old, - new=new, - 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 - ----- - 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 - exiting the ``hold_trait_notifications`` context, and such changes may not - 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. - """ - 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): - """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 - - -class HasDescriptors(six.with_metaclass(MetaHasDescriptors, object)): - """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: - inst = new_meth(cls, *args, **kwargs) - inst.setup_instance(*args, **kwargs) - 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) - - -class HasTraits(six.with_metaclass(MetaHasTraits, HasDescriptors)): - - def setup_instance(self, *args, **kwargs): - 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. - super_args = args - super_kwargs = {} - with self.hold_trait_notifications(): - for key, value in kwargs.items(): - if self.has_trait(key): - setattr(self, key, value) - else: - # passthrough args that don't set traits to super - super_kwargs[key] = value - try: - super(HasTraits, self).__init__(*super_args, **super_kwargs) - except TypeError as e: - arg_s_list = [ repr(arg) for arg in super_args ] - for k, v in super_kwargs.items(): - arg_s_list.append("%s=%r" % (k, v)) - arg_s = ', '.join(arg_s_list) - warn( - "Passing unrecoginized arguments to super({classname}).__init__({arg_s}).\n" - "{error}\n" - "This is deprecated in traitlets 4.2." - "This error will be raised in a future release of traitlets." - .format( - arg_s=arg_s, classname=self.__class__.__name__, - error=e, - ), - 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) - - @property - @contextlib.contextmanager - def cross_validation_lock(self): - """ - A contextmanager for running a block with our cross validation lock set - to True. - - At the end of the block, the lock's value is restored to its value - prior to entering the block. - """ - if self._cross_validation_lock: - yield - return - else: - try: - self._cross_validation_lock = True - yield - finally: - 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. - """ - 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: - 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): - 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)) - 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. - 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 - # 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): - self.notify_change(Bunch( - name=name, - old=old_value, - new=new_value, - owner=self, - type='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__ - 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. - """ - 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 - 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. - - 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): - """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 - TraitError is raised and no new validator is registered. - - 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 - - def add_traits(self, **traits): - """Dynamically add trait attributes to the HasTraits instance.""" - self.__class__ = type(self.__class__.__name__, (self.__class__,), - traits) - for trait in traits.values(): - trait.instance_init(self) - - def set_trait(self, name, value): - """Forcibly sets trait attribute, including read-only attributes.""" - cls = self.__class__ - if not self.has_trait(name): - raise TraitError("Class %s does not have a trait named %s" % - (cls.__name__, name)) - 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. - """ - 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.""" - 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)) - 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) - - @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. - - Parameters - ---------- - name: str (default: None) - The name of a trait of this class. If name is ``None`` then all - the event handlers of this class will be returned instead. - - Returns - ------- - The event handlers associated with a trait name, or all event handlers. - """ - events = {} - for k, v in getmembers(cls): - if isinstance(v, EventHandler): - if name is None: - events[k] = v - elif name in v.trait_names: - events[k] = v - elif hasattr(v, 'tags'): - if cls.trait_names(**v.tags): - 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) - 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.""" - - 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 - - if not (inspect.isclass(klass) or isinstance(klass, six.string_types)): - 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.""" - 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.""" - if isinstance(self.klass, six.string_types): - 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): - if isinstance(self.klass, six.string_types): - 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 - 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 - - 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 - - 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 - - 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): - 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): - 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' - - 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 __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 - 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): - with obj.cross_validation_lock: - 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]) - - def make_dynamic_default(self): - if self.default_value is not Undefined: - return self.default_value - for trait_type in self.trait_types: - if trait_type.default_value is not Undefined: - return trait_type.default_value - elif hasattr(trait_type, 'make_dynamic_default'): - 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' - - -def _validate_bounds(trait, obj, value): - """ - Validate that a number to be applied to a trait is between bounds. - - If value is not between min_bound and max_bound, this raises a - TraitError with an error message appropriate for this trait. - """ - if trait.min is not None and value < trait.min: - raise TraitError( - "The value of the '{name}' trait of {klass} instance should " - "not be less than {min_bound}, but a value of {value} was " - "specified".format( - name=trait.name, klass=class_of(obj), - value=value, min_bound=trait.min)) - if trait.max is not None and value > trait.max: - raise TraitError( - "The value of the '{name}' trait of {klass} instance should " - "not be greater than {max_bound}, but a value of {value} was " - "specified".format( - name=trait.name, klass=class_of(obj), - value=value, max_bound=trait.max)) - return value - - -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) - return _validate_bounds(self, obj, value) - - -class CInt(Int): - """A casting version of the int trait.""" - - def validate(self, obj, value): - try: - value = int(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' - - def __init__(self, default_value=Undefined, allow_none=False, **kwargs): - self.min = kwargs.pop('min', None) - self.max = kwargs.pop('max', None) - super(Long, self).__init__( - default_value=default_value, - 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) - - 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: - value = long(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' - - def __init__(self, default_value=Undefined, allow_none=False, **kwargs): - self.min = kwargs.pop('min', None) - self.max = kwargs.pop('max', None) - super(Integer, self).__init__( - default_value=default_value, - 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) - - 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' - - def __init__(self, default_value=Undefined, allow_none=False, **kwargs): - 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) - return _validate_bounds(self, obj, value) - - -class CFloat(Float): - """A casting version of the float trait.""" - - def validate(self, obj, value): - try: - value = float(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): - 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 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" - - 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 - else: - coerce_str = staticmethod(lambda _,s: s) - - 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) - - 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.""" - - def __init__(self, values, default_value=Undefined, **kwargs): - self.values = values - if kwargs.get('allow_none', False) and default_value is Undefined: - 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 __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): - 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 - - 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 - - **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): - 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)) - - 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 __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, - **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, - **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 - """ - super(Set, self).__init__(trait, default_value, minlen, maxlen, **kwargs) - - -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. - """ - 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): - 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 - 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, - **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``. - - Parameters - ---------- - - 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): - 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 - - 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): - use_dict = bool(self._traits) - default_to = (self._trait or Any()) - if not use_dict and isinstance(default_to, Any): - return 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: - v = value[key] - if not isinstance(validate_with, Any): - v = validate_with._validate(obj, v) - 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: - 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) - - -class UseEnum(TraitType): - """Use a Enum class as model for the data type description. - Note that if no default-value is provided, the first enum-value is used - as default-value. - - .. sourcecode:: python - - # -- SINCE: Python 3.4 (or install backport: pip install enum34) - import enum - from traitlets import HasTraits, UseEnum - - class Color(enum.Enum): - red = 1 # -- IMPLICIT: default_value - blue = 2 - green = 3 - - class MyEntity(HasTraits): - color = UseEnum(Color, default_value=Color.blue) - - entity = MyEntity(color=Color.red) - entity.color = Color.green # USE: Enum-value (preferred) - entity.color = "green" # USE: name (as string) - entity.color = "Color.green" # USE: scoped-name (as string) - entity.color = 3 # USE: number (as int) - assert entity.color is Color.green - """ - default_value = None - info_text = "Trait type adapter to a Enum class" - - def __init__(self, enum_class, default_value=None, **kwargs): - assert issubclass(enum_class, enum.Enum), \ - "REQUIRE: enum.Enum, but was: %r" % enum_class - allow_none = kwargs.get("allow_none", False) - if default_value is None and not allow_none: - default_value = list(enum_class.__members__.values())[0] - super(UseEnum, self).__init__(default_value=default_value, **kwargs) - self.enum_class = enum_class - self.name_prefix = enum_class.__name__ + "." - - def select_by_number(self, value, default=Undefined): - """Selects enum-value by using its number-constant.""" - assert isinstance(value, int) - enum_members = self.enum_class.__members__ - for enum_item in enum_members.values(): - if enum_item.value == value: - return enum_item - # -- NOT FOUND: - return default - - def select_by_name(self, value, default=Undefined): - """Selects enum-value by using its name or scoped-name.""" - assert isinstance(value, six.string_types) - if value.startswith(self.name_prefix): - # -- SUPPORT SCOPED-NAMES, like: "Color.red" => "red" - value = value.replace(self.name_prefix, "", 1) - return self.enum_class.__members__.get(value, default) - - def validate(self, obj, value): - if isinstance(value, self.enum_class): - return value - elif isinstance(value, int): - # -- CONVERT: number => enum_value (item) - value2 = self.select_by_number(value) - if value2 is not Undefined: - return value2 - elif isinstance(value, six.string_types): - # -- CONVERT: name or scoped_name (as string) => enum_value (item) - value2 = self.select_by_name(value) - if value2 is not Undefined: - return value2 - elif value is None: - if self.allow_none: - return None - else: - return self.default_value - self.error(obj, value) - - def info(self): - """Returns a description of this Enum trait (in case of errors).""" - result = "Any of: %s" % ", ".join(self.enum_class.__members__.keys()) - if self.allow_none: - return result + " or None" - return result diff --git a/contrib/python/traitlets/py2/traitlets/utils/__init__.py b/contrib/python/traitlets/py2/traitlets/utils/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 --- a/contrib/python/traitlets/py2/traitlets/utils/__init__.py +++ /dev/null diff --git a/contrib/python/traitlets/py2/traitlets/utils/bunch.py b/contrib/python/traitlets/py2/traitlets/utils/bunch.py deleted file mode 100644 index 2edb830ad63..00000000000 --- a/contrib/python/traitlets/py2/traitlets/utils/bunch.py +++ /dev/null @@ -1,25 +0,0 @@ -"""Yet another implementation of bunch - -attribute-access of items on a dict. -""" - -# Copyright (c) Jupyter Development Team. -# Distributed under the terms of the Modified BSD License. - -class Bunch(dict): - """A dict with attribute-access""" - def __getattr__(self, key): - try: - return self.__getitem__(key) - except KeyError: - raise AttributeError(key) - - def __setattr__(self, key, value): - self.__setitem__(key, value) - - def __dir__(self): - # py2-compat: can't use super because dict doesn't have __dir__ - names = dir({}) - names.extend(self.keys()) - return names - diff --git a/contrib/python/traitlets/py2/traitlets/utils/getargspec.py b/contrib/python/traitlets/py2/traitlets/utils/getargspec.py deleted file mode 100644 index 0a047379fec..00000000000 --- a/contrib/python/traitlets/py2/traitlets/utils/getargspec.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- 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) diff --git a/contrib/python/traitlets/py2/traitlets/utils/importstring.py b/contrib/python/traitlets/py2/traitlets/utils/importstring.py deleted file mode 100644 index 5b4f643f418..00000000000 --- a/contrib/python/traitlets/py2/traitlets/utils/importstring.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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]) diff --git a/contrib/python/traitlets/py2/traitlets/utils/sentinel.py b/contrib/python/traitlets/py2/traitlets/utils/sentinel.py deleted file mode 100644 index dc57a2591ca..00000000000 --- a/contrib/python/traitlets/py2/traitlets/utils/sentinel.py +++ /dev/null @@ -1,17 +0,0 @@ -"""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/traitlets/utils/tests/__init__.py b/contrib/python/traitlets/py2/traitlets/utils/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 --- a/contrib/python/traitlets/py2/traitlets/utils/tests/__init__.py +++ /dev/null diff --git a/contrib/python/traitlets/py2/traitlets/utils/tests/test_bunch.py b/contrib/python/traitlets/py2/traitlets/utils/tests/test_bunch.py deleted file mode 100644 index ef5c3c1384f..00000000000 --- a/contrib/python/traitlets/py2/traitlets/utils/tests/test_bunch.py +++ /dev/null @@ -1,14 +0,0 @@ -from ..bunch import Bunch - -def test_bunch(): - b = Bunch(x=5, y=10) - assert 'y' in b - assert 'x' in b - assert b.x == 5 - b['a'] = 'hi' - assert b.a == 'hi' - -def test_bunch_dir(): - b = Bunch(x=5, y=10) - assert 'x' in dir(b) - assert 'keys' in dir(b) diff --git a/contrib/python/traitlets/py2/traitlets/utils/tests/test_importstring.py b/contrib/python/traitlets/py2/traitlets/utils/tests/test_importstring.py deleted file mode 100644 index c86459ff423..00000000000 --- a/contrib/python/traitlets/py2/traitlets/utils/tests/test_importstring.py +++ /dev/null @@ -1,30 +0,0 @@ -# encoding: utf-8 -# 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. -"""Tests for traitlets.utils.importstring.""" - -import os -from unittest import TestCase - -from ..importstring import import_item - - -class TestImportItem(TestCase): - - def test_import_unicode(self): - self.assertIs(os, import_item(u'os')) - self.assertIs(os.path, import_item(u'os.path')) - self.assertIs(os.path.join, import_item(u'os.path.join')) - - def test_bad_input(self): - class NotAString(object): - pass - msg = ( - "import_item accepts strings, " - "not '%s'." % NotAString - ) - with self.assertRaisesRegexp(TypeError, msg): - import_item(NotAString()) |
