1"""
2A lightweight Traits like module.
3
4This is designed to provide a lightweight, simple, pure Python version of
5many of the capabilities of enthought.traits.  This includes:
6
7* Validation
8* Type specification with defaults
9* Static and dynamic notification
10* Basic predefined types
11* An API that is similar to enthought.traits
12
13We don't support:
14
15* Delegation
16* Automatic GUI generation
17* A full set of trait types.  Most importantly, we don't provide container
18  traits (list, dict, tuple) that can trigger notifications if their
19  contents change.
20* API compatibility with enthought.traits
21
22There are also some important difference in our design:
23
24* enthought.traits does not validate default values.  We do.
25
26We choose to create this module because we need these capabilities, but
27we need them to be pure Python so they work in all Python implementations,
28including Jython and IronPython.
29
30Inheritance diagram:
31
32.. inheritance-diagram:: traitlets.traitlets
33   :parts: 3
34"""
35
36# Copyright (c) IPython Development Team.
37# Distributed under the terms of the Modified BSD License.
38#
39# Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
40# also under the terms of the Modified BSD License.
41
42from ast import literal_eval
43import contextlib
44import inspect
45import os
46import re
47import sys
48import types
49import enum
50from warnings import warn, warn_explicit
51
52from .utils.getargspec import getargspec
53from .utils.importstring import import_item
54from .utils.sentinel import Sentinel
55from .utils.bunch import Bunch
56from .utils.descriptions import describe, class_of, add_article, repr_type
57
58SequenceTypes = (list, tuple, set, frozenset)
59
60# backward compatibility, use to differ between Python 2 and 3.
61ClassTypes = (type,)
62
63# exports:
64
65__all__ = [
66    "default",
67    "validate",
68    "observe",
69    "observe_compat",
70    "link",
71    "directional_link",
72    "dlink",
73    "Undefined",
74    "All",
75    "NoDefaultSpecified",
76    "TraitError",
77    "HasDescriptors",
78    "HasTraits",
79    "MetaHasDescriptors",
80    "MetaHasTraits",
81    "BaseDescriptor",
82    "TraitType",
83    "parse_notifier_name",
84]
85
86# any TraitType subclass (that doesn't start with _) will be added automatically
87
88#-----------------------------------------------------------------------------
89# Basic classes
90#-----------------------------------------------------------------------------
91
92
93Undefined = Sentinel('Undefined', 'traitlets',
94'''
95Used in Traitlets to specify that no defaults are set in kwargs
96'''
97)
98
99All = Sentinel('All', 'traitlets',
100'''
101Used in Traitlets to listen to all types of notification or to notifications
102from all trait attributes.
103'''
104)
105
106# Deprecated alias
107NoDefaultSpecified = Undefined
108
109class TraitError(Exception):
110    pass
111
112#-----------------------------------------------------------------------------
113# Utilities
114#-----------------------------------------------------------------------------
115
116_name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
117
118def isidentifier(s):
119    return s.isidentifier()
120
121_deprecations_shown = set()
122def _should_warn(key):
123    """Add our own checks for too many deprecation warnings.
124
125    Limit to once per package.
126    """
127    env_flag = os.environ.get('TRAITLETS_ALL_DEPRECATIONS')
128    if env_flag and env_flag != '0':
129        return True
130
131    if key not in _deprecations_shown:
132        _deprecations_shown.add(key)
133        return True
134    else:
135        return False
136
137def _deprecated_method(method, cls, method_name, msg):
138    """Show deprecation warning about a magic method definition.
139
140    Uses warn_explicit to bind warning to method definition instead of triggering code,
141    which isn't relevant.
142    """
143    warn_msg = "{classname}.{method_name} is deprecated in traitlets 4.1: {msg}".format(
144        classname=cls.__name__, method_name=method_name, msg=msg
145    )
146
147    for parent in inspect.getmro(cls):
148        if method_name in parent.__dict__:
149            cls = parent
150            break
151    # limit deprecation messages to once per package
152    package_name = cls.__module__.split('.', 1)[0]
153    key = (package_name, msg)
154    if not _should_warn(key):
155        return
156    try:
157        fname = inspect.getsourcefile(method) or "<unknown>"
158        lineno = inspect.getsourcelines(method)[1] or 0
159    except (OSError, TypeError) as e:
160        # Failed to inspect for some reason
161        warn(warn_msg + ('\n(inspection failed) %s' % e), DeprecationWarning)
162    else:
163        warn_explicit(warn_msg, DeprecationWarning, fname, lineno)
164
165def _safe_literal_eval(s):
166    """Safely evaluate an expression
167
168    Returns original string if eval fails.
169
170    Use only where types are ambiguous.
171    """
172    try:
173        return literal_eval(s)
174    except (NameError, SyntaxError, ValueError):
175        return s
176
177def is_trait(t):
178    """ Returns whether the given value is an instance or subclass of TraitType.
179    """
180    return (isinstance(t, TraitType) or
181            (isinstance(t, type) and issubclass(t, TraitType)))
182
183
184def parse_notifier_name(names):
185    """Convert the name argument to a list of names.
186
187    Examples
188    --------
189    >>> parse_notifier_name([])
190    [All]
191    >>> parse_notifier_name("a")
192    ['a']
193    >>> parse_notifier_name(["a", "b"])
194    ['a', 'b']
195    >>> parse_notifier_name(All)
196    [All]
197    """
198    if names is All or isinstance(names, str):
199        return [names]
200    else:
201        if not names or All in names:
202            return [All]
203        for n in names:
204            if not isinstance(n, str):
205                raise TypeError("names must be strings, not %r" % n)
206        return names
207
208
209class _SimpleTest:
210    def __init__ ( self, value ): self.value = value
211    def __call__ ( self, test  ):
212        return test == self.value
213    def __repr__(self):
214        return "<SimpleTest(%r)" % self.value
215    def __str__(self):
216        return self.__repr__()
217
218
219def getmembers(object, predicate=None):
220    """A safe version of inspect.getmembers that handles missing attributes.
221
222    This is useful when there are descriptor based attributes that for
223    some reason raise AttributeError even though they exist.  This happens
224    in zope.inteface with the __provides__ attribute.
225    """
226    results = []
227    for key in dir(object):
228        try:
229            value = getattr(object, key)
230        except AttributeError:
231            pass
232        else:
233            if not predicate or predicate(value):
234                results.append((key, value))
235    results.sort()
236    return results
237
238def _validate_link(*tuples):
239    """Validate arguments for traitlet link functions"""
240    for t in tuples:
241        if not len(t) == 2:
242            raise TypeError("Each linked traitlet must be specified as (HasTraits, 'trait_name'), not %r" % t)
243        obj, trait_name = t
244        if not isinstance(obj, HasTraits):
245            raise TypeError("Each object must be HasTraits, not %r" % type(obj))
246        if not trait_name in obj.traits():
247            raise TypeError("%r has no trait %r" % (obj, trait_name))
248
249class link(object):
250    """Link traits from different objects together so they remain in sync.
251
252    Parameters
253    ----------
254    source : (object / attribute name) pair
255    target : (object / attribute name) pair
256    transform: iterable with two callables (optional)
257        Data transformation between source and target and target and source.
258
259    Examples
260    --------
261    >>> c = link((src, "value"), (tgt, "value"))
262    >>> src.value = 5  # updates other objects as well
263    """
264    updating = False
265
266    def __init__(self, source, target, transform=None):
267        _validate_link(source, target)
268        self.source, self.target = source, target
269        self._transform, self._transform_inv = (
270            transform if transform else (lambda x: x,) * 2)
271
272        self.link()
273
274    def link(self):
275        try:
276            setattr(self.target[0], self.target[1],
277                    self._transform(getattr(self.source[0], self.source[1])))
278
279        finally:
280            self.source[0].observe(self._update_target, names=self.source[1])
281            self.target[0].observe(self._update_source, names=self.target[1])
282
283    @contextlib.contextmanager
284    def _busy_updating(self):
285        self.updating = True
286        try:
287            yield
288        finally:
289            self.updating = False
290
291    def _update_target(self, change):
292        if self.updating:
293            return
294        with self._busy_updating():
295            setattr(self.target[0], self.target[1], self._transform(change.new))
296            if getattr(self.source[0], self.source[1]) != change.new:
297                raise TraitError(
298                    "Broken link {}: the source value changed while updating "
299                    "the target.".format(self))
300
301    def _update_source(self, change):
302        if self.updating:
303            return
304        with self._busy_updating():
305            setattr(self.source[0], self.source[1],
306                    self._transform_inv(change.new))
307            if getattr(self.target[0], self.target[1]) != change.new:
308                raise TraitError(
309                    "Broken link {}: the target value changed while updating "
310                    "the source.".format(self))
311
312    def unlink(self):
313        self.source[0].unobserve(self._update_target, names=self.source[1])
314        self.target[0].unobserve(self._update_source, names=self.target[1])
315
316
317class directional_link(object):
318    """Link the trait of a source object with traits of target objects.
319
320    Parameters
321    ----------
322    source : (object, attribute name) pair
323    target : (object, attribute name) pair
324    transform: callable (optional)
325        Data transformation between source and target.
326
327    Examples
328    --------
329    >>> c = directional_link((src, "value"), (tgt, "value"))
330    >>> src.value = 5  # updates target objects
331    >>> tgt.value = 6  # does not update source object
332    """
333    updating = False
334
335    def __init__(self, source, target, transform=None):
336        self._transform = transform if transform else lambda x: x
337        _validate_link(source, target)
338        self.source, self.target = source, target
339        self.link()
340
341    def link(self):
342        try:
343            setattr(self.target[0], self.target[1],
344                    self._transform(getattr(self.source[0], self.source[1])))
345        finally:
346            self.source[0].observe(self._update, names=self.source[1])
347
348    @contextlib.contextmanager
349    def _busy_updating(self):
350        self.updating = True
351        try:
352            yield
353        finally:
354            self.updating = False
355
356    def _update(self, change):
357        if self.updating:
358            return
359        with self._busy_updating():
360            setattr(self.target[0], self.target[1],
361                    self._transform(change.new))
362
363    def unlink(self):
364        self.source[0].unobserve(self._update, names=self.source[1])
365
366dlink = directional_link
367
368
369#-----------------------------------------------------------------------------
370# Base Descriptor Class
371#-----------------------------------------------------------------------------
372
373
374class BaseDescriptor(object):
375    """Base descriptor class
376
377    Notes
378    -----
379    This implements Python's descriptor protocol.
380
381    This class is the base class for all such descriptors.  The
382    only magic we use is a custom metaclass for the main :class:`HasTraits`
383    class that does the following:
384
385    1. Sets the :attr:`name` attribute of every :class:`BaseDescriptor`
386       instance in the class dict to the name of the attribute.
387    2. Sets the :attr:`this_class` attribute of every :class:`BaseDescriptor`
388       instance in the class dict to the *class* that declared the trait.
389       This is used by the :class:`This` trait to allow subclasses to
390       accept superclasses for :class:`This` values.
391    """
392
393    name = None
394    this_class = None
395
396    def class_init(self, cls, name):
397        """Part of the initialization which may depend on the underlying
398        HasDescriptors class.
399
400        It is typically overloaded for specific types.
401
402        This method is called by :meth:`MetaHasDescriptors.__init__`
403        passing the class (`cls`) and `name` under which the descriptor
404        has been assigned.
405        """
406        self.this_class = cls
407        self.name = name
408
409    def subclass_init(self, cls):
410        pass
411
412    def instance_init(self, obj):
413        """Part of the initialization which may depend on the underlying
414        HasDescriptors instance.
415
416        It is typically overloaded for specific types.
417
418        This method is called by :meth:`HasTraits.__new__` and in the
419        :meth:`BaseDescriptor.instance_init` method of descriptors holding
420        other descriptors.
421        """
422        pass
423
424
425class TraitType(BaseDescriptor):
426    """A base class for all trait types.
427    """
428
429    metadata = {}
430    allow_none = False
431    read_only = False
432    info_text = 'any value'
433    default_value = Undefined
434
435    def __init__(self, default_value=Undefined, allow_none=False, read_only=None, help=None,
436        config=None, **kwargs):
437        """Declare a traitlet.
438
439        If *allow_none* is True, None is a valid value in addition to any
440        values that are normally valid. The default is up to the subclass.
441        For most trait types, the default value for ``allow_none`` is False.
442
443        If *read_only* is True, attempts to directly modify a trait attribute raises a TraitError.
444
445        Extra metadata can be associated with the traitlet using the .tag() convenience method
446        or by using the traitlet instance's .metadata dictionary.
447        """
448        if default_value is not Undefined:
449            self.default_value = default_value
450        if allow_none:
451            self.allow_none = allow_none
452        if read_only is not None:
453            self.read_only = read_only
454        self.help = help if help is not None else ''
455
456        if len(kwargs) > 0:
457            stacklevel = 1
458            f = inspect.currentframe()
459            # count supers to determine stacklevel for warning
460            while f.f_code.co_name == '__init__':
461                stacklevel += 1
462                f = f.f_back
463            mod = f.f_globals.get('__name__') or ''
464            pkg = mod.split('.', 1)[0]
465            key = tuple(['metadata-tag', pkg] + sorted(kwargs))
466            if _should_warn(key):
467                warn("metadata %s was set from the constructor. "
468                     "With traitlets 4.1, metadata should be set using the .tag() method, "
469                     "e.g., Int().tag(key1='value1', key2='value2')" % (kwargs,),
470                     DeprecationWarning, stacklevel=stacklevel)
471            if len(self.metadata) > 0:
472                self.metadata = self.metadata.copy()
473                self.metadata.update(kwargs)
474            else:
475                self.metadata = kwargs
476        else:
477            self.metadata = self.metadata.copy()
478        if config is not None:
479            self.metadata['config'] = config
480
481        # We add help to the metadata during a deprecation period so that
482        # code that looks for the help string there can find it.
483        if help is not None:
484            self.metadata['help'] = help
485
486    def from_string(self, s):
487        """Get a value from a config string
488
489        such as an environment variable or CLI arguments.
490
491        Traits can override this method to define their own
492        parsing of config strings.
493
494        .. seealso:: item_from_string
495
496        .. versionadded:: 5.0
497        """
498        if self.allow_none and s == 'None':
499            return None
500        return s
501
502    def default(self, obj=None):
503        """The default generator for this trait
504
505        Notes
506        -----
507        This method is registered to HasTraits classes during ``class_init``
508        in the same way that dynamic defaults defined by ``@default`` are.
509        """
510        if self.default_value is not Undefined:
511            return self.default_value
512        elif hasattr(self, 'make_dynamic_default'):
513            return self.make_dynamic_default()
514        else:
515            # Undefined will raise in TraitType.get
516            return self.default_value
517
518    def get_default_value(self):
519        """DEPRECATED: Retrieve the static default value for this trait.
520        Use self.default_value instead
521        """
522        warn("get_default_value is deprecated in traitlets 4.0: use the .default_value attribute", DeprecationWarning,
523             stacklevel=2)
524        return self.default_value
525
526    def init_default_value(self, obj):
527        """DEPRECATED: Set the static default value for the trait type.
528        """
529        warn("init_default_value is deprecated in traitlets 4.0, and may be removed in the future", DeprecationWarning,
530             stacklevel=2)
531        value = self._validate(obj, self.default_value)
532        obj._trait_values[self.name] = value
533        return value
534
535    def get(self, obj, cls=None):
536        try:
537            value = obj._trait_values[self.name]
538        except KeyError:
539            # Check for a dynamic initializer.
540            default = obj.trait_defaults(self.name)
541            if default is Undefined:
542                warn(
543                    "Explicit using of Undefined as the default value "
544                    "is deprecated in traitlets 5.0, and may cause "
545                    "exceptions in the future.",
546                    DeprecationWarning,
547                    stacklevel=2
548                )
549            with obj.cross_validation_lock:
550                value = self._validate(obj, default)
551            obj._trait_values[self.name] = value
552            obj._notify_observers(Bunch(
553                name=self.name,
554                value=value,
555                owner=obj,
556                type='default',
557            ))
558            return value
559        except Exception:
560            # This should never be reached.
561            raise TraitError('Unexpected error in TraitType: '
562                             'default value not set properly')
563        else:
564            return value
565
566    def __get__(self, obj, cls=None):
567        """Get the value of the trait by self.name for the instance.
568
569        Default values are instantiated when :meth:`HasTraits.__new__`
570        is called.  Thus by the time this method gets called either the
571        default value or a user defined value (they called :meth:`__set__`)
572        is in the :class:`HasTraits` instance.
573        """
574        if obj is None:
575            return self
576        else:
577            return self.get(obj, cls)
578
579    def set(self, obj, value):
580        new_value = self._validate(obj, value)
581        try:
582            old_value = obj._trait_values[self.name]
583        except KeyError:
584            old_value = self.default_value
585
586        obj._trait_values[self.name] = new_value
587        try:
588            silent = bool(old_value == new_value)
589        except Exception:
590            # if there is an error in comparing, default to notify
591            silent = False
592        if silent is not True:
593            # we explicitly compare silent to True just in case the equality
594            # comparison above returns something other than True/False
595            obj._notify_trait(self.name, old_value, new_value)
596
597    def __set__(self, obj, value):
598        """Set the value of the trait by self.name for the instance.
599
600        Values pass through a validation stage where errors are raised when
601        impropper types, or types that cannot be coerced, are encountered.
602        """
603        if self.read_only:
604            raise TraitError('The "%s" trait is read-only.' % self.name)
605        else:
606            self.set(obj, value)
607
608    def _validate(self, obj, value):
609        if value is None and self.allow_none:
610            return value
611        if hasattr(self, 'validate'):
612            value = self.validate(obj, value)
613        if obj._cross_validation_lock is False:
614            value = self._cross_validate(obj, value)
615        return value
616
617    def _cross_validate(self, obj, value):
618        if self.name in obj._trait_validators:
619            proposal = Bunch({'trait': self, 'value': value, 'owner': obj})
620            value = obj._trait_validators[self.name](obj, proposal)
621        elif hasattr(obj, '_%s_validate' % self.name):
622            meth_name = '_%s_validate' % self.name
623            cross_validate = getattr(obj, meth_name)
624            _deprecated_method(cross_validate, obj.__class__, meth_name,
625                "use @validate decorator instead.")
626            value = cross_validate(value, self)
627        return value
628
629    def __or__(self, other):
630        if isinstance(other, Union):
631            return Union([self] + other.trait_types)
632        else:
633            return Union([self, other])
634
635    def info(self):
636        return self.info_text
637
638    def error(self, obj, value, error=None, info=None):
639        """Raise a TraitError
640
641        Parameters
642        ----------
643        obj : HasTraits or None
644            The instance which owns the trait. If not
645            object is given, then an object agnostic
646            error will be raised.
647        value : any
648            The value that caused the error.
649        error : Exception (default: None)
650            An error that was raised by a child trait.
651            The arguments of this exception should be
652            of the form ``(value, info, *traits)``.
653            Where the ``value`` and ``info`` are the
654            problem value, and string describing the
655            expected value. The ``traits`` are a series
656            of :class:`TraitType` instances that are
657            "children" of this one (the first being
658            the deepest).
659        info : str (default: None)
660            A description of the expected value. By
661            default this is infered from this trait's
662            ``info`` method.
663        """
664        if error is not None:
665            # handle nested error
666            error.args += (self,)
667            if self.name is not None:
668                # this is the root trait that must format the final message
669                chain = " of ".join(describe("a", t) for t in error.args[2:])
670                if obj is not None:
671                    error.args = ("The '%s' trait of %s instance contains %s which "
672                        "expected %s, not %s." % (self.name, describe("an", obj),
673                        chain, error.args[1], describe("the", error.args[0])),)
674                else:
675                    error.args = ("The '%s' trait contains %s which "
676                        "expected %s, not %s." % (self.name, chain,
677                        error.args[1], describe("the", error.args[0])),)
678            raise error
679        else:
680            # this trait caused an error
681            if self.name is None:
682                # this is not the root trait
683                raise TraitError(value, info or self.info(), self)
684            else:
685                # this is the root trait
686                if obj is not None:
687                    e = "The '%s' trait of %s instance expected %s, not %s." % (
688                        self.name, class_of(obj), self.info(), describe("the", value))
689                else:
690                    e = "The '%s' trait expected %s, not %s." % (
691                        self.name, self.info(), describe("the", value))
692                raise TraitError(e)
693
694    def get_metadata(self, key, default=None):
695        """DEPRECATED: Get a metadata value.
696
697        Use .metadata[key] or .metadata.get(key, default) instead.
698        """
699        if key == 'help':
700            msg = "use the instance .help string directly, like x.help"
701        else:
702            msg = "use the instance .metadata dictionary directly, like x.metadata[key] or x.metadata.get(key, default)"
703        warn("Deprecated in traitlets 4.1, " + msg, DeprecationWarning, stacklevel=2)
704        return self.metadata.get(key, default)
705
706    def set_metadata(self, key, value):
707        """DEPRECATED: Set a metadata key/value.
708
709        Use .metadata[key] = value instead.
710        """
711        if key == 'help':
712            msg = "use the instance .help string directly, like x.help = value"
713        else:
714            msg = "use the instance .metadata dictionary directly, like x.metadata[key] = value"
715        warn("Deprecated in traitlets 4.1, " + msg, DeprecationWarning, stacklevel=2)
716        self.metadata[key] = value
717
718    def tag(self, **metadata):
719        """Sets metadata and returns self.
720
721        This allows convenient metadata tagging when initializing the trait, such as:
722
723        >>> Int(0).tag(config=True, sync=True)
724        """
725        maybe_constructor_keywords = set(metadata.keys()).intersection({'help','allow_none', 'read_only', 'default_value'})
726        if maybe_constructor_keywords:
727            warn('The following attributes are set in using `tag`, but seem to be constructor keywords arguments: %s '%
728                    maybe_constructor_keywords, UserWarning, stacklevel=2)
729
730        self.metadata.update(metadata)
731        return self
732
733    def default_value_repr(self):
734        return repr(self.default_value)
735
736#-----------------------------------------------------------------------------
737# The HasTraits implementation
738#-----------------------------------------------------------------------------
739
740class _CallbackWrapper(object):
741    """An object adapting a on_trait_change callback into an observe callback.
742
743    The comparison operator __eq__ is implemented to enable removal of wrapped
744    callbacks.
745    """
746
747    def __init__(self, cb):
748        self.cb = cb
749        # Bound methods have an additional 'self' argument.
750        offset = -1 if isinstance(self.cb, types.MethodType) else 0
751        self.nargs = len(getargspec(cb)[0]) + offset
752        if (self.nargs > 4):
753            raise TraitError('a trait changed callback must have 0-4 arguments.')
754
755    def __eq__(self, other):
756        # The wrapper is equal to the wrapped element
757        if isinstance(other, _CallbackWrapper):
758            return self.cb == other.cb
759        else:
760            return self.cb == other
761
762    def __call__(self, change):
763        # The wrapper is callable
764        if self.nargs == 0:
765            self.cb()
766        elif self.nargs == 1:
767            self.cb(change.name)
768        elif self.nargs == 2:
769            self.cb(change.name, change.new)
770        elif self.nargs == 3:
771            self.cb(change.name, change.old, change.new)
772        elif self.nargs == 4:
773            self.cb(change.name, change.old, change.new, change.owner)
774
775def _callback_wrapper(cb):
776    if isinstance(cb, _CallbackWrapper):
777        return cb
778    else:
779        return _CallbackWrapper(cb)
780
781
782class MetaHasDescriptors(type):
783    """A metaclass for HasDescriptors.
784
785    This metaclass makes sure that any TraitType class attributes are
786    instantiated and sets their name attribute.
787    """
788
789    def __new__(mcls, name, bases, classdict):
790        """Create the HasDescriptors class."""
791        for k, v in classdict.items():
792            # ----------------------------------------------------------------
793            # Support of deprecated behavior allowing for TraitType types
794            # to be used instead of TraitType instances.
795            if inspect.isclass(v) and issubclass(v, TraitType):
796                warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)."
797                     " Passing types is deprecated in traitlets 4.1.",
798                     DeprecationWarning, stacklevel=2)
799                classdict[k] = v()
800            # ----------------------------------------------------------------
801
802        return super(MetaHasDescriptors, mcls).__new__(mcls, name, bases, classdict)
803
804    def __init__(cls, name, bases, classdict):
805        """Finish initializing the HasDescriptors class."""
806        super(MetaHasDescriptors, cls).__init__(name, bases, classdict)
807        cls.setup_class(classdict)
808
809    def setup_class(cls, classdict):
810        """Setup descriptor instance on the class
811
812        This sets the :attr:`this_class` and :attr:`name` attributes of each
813        BaseDescriptor in the class dict of the newly created ``cls`` before
814        calling their :attr:`class_init` method.
815        """
816        for k, v in classdict.items():
817            if isinstance(v, BaseDescriptor):
818                v.class_init(cls, k)
819
820        for k, v in getmembers(cls):
821            if isinstance(v, BaseDescriptor):
822                v.subclass_init(cls)
823
824
825class MetaHasTraits(MetaHasDescriptors):
826    """A metaclass for HasTraits."""
827
828    def setup_class(cls, classdict):
829        cls._trait_default_generators = {}
830        super(MetaHasTraits, cls).setup_class(classdict)
831
832
833def observe(*names, type="change"):
834    """A decorator which can be used to observe Traits on a class.
835
836    The handler passed to the decorator will be called with one ``change``
837    dict argument. The change dictionary at least holds a 'type' key and a
838    'name' key, corresponding respectively to the type of notification and the
839    name of the attribute that triggered the notification.
840
841    Other keys may be passed depending on the value of 'type'. In the case
842    where type is 'change', we also have the following keys:
843    * ``owner`` : the HasTraits instance
844    * ``old`` : the old value of the modified trait attribute
845    * ``new`` : the new value of the modified trait attribute
846    * ``name`` : the name of the modified trait attribute.
847
848    Parameters
849    ----------
850    *names
851        The str names of the Traits to observe on the object.
852    type : str, kwarg-only
853        The type of event to observe (e.g. 'change')
854    """
855    if not names:
856        raise TypeError("Please specify at least one trait name to observe.")
857    for name in names:
858        if name is not All and not isinstance(name, str):
859            raise TypeError("trait names to observe must be strings or All, not %r" % name)
860    return ObserveHandler(names, type=type)
861
862
863def observe_compat(func):
864    """Backward-compatibility shim decorator for observers
865
866    Use with:
867
868    @observe('name')
869    @observe_compat
870    def _foo_changed(self, change):
871        ...
872
873    With this, `super()._foo_changed(self, name, old, new)` in subclasses will still work.
874    Allows adoption of new observer API without breaking subclasses that override and super.
875    """
876    def compatible_observer(self, change_or_name, old=Undefined, new=Undefined):
877        if isinstance(change_or_name, dict):
878            change = change_or_name
879        else:
880            clsname = self.__class__.__name__
881            warn("A parent of %s._%s_changed has adopted the new (traitlets 4.1) @observe(change) API" % (
882                clsname, change_or_name), DeprecationWarning)
883            change = Bunch(
884                type='change',
885                old=old,
886                new=new,
887                name=change_or_name,
888                owner=self,
889            )
890        return func(self, change)
891    return compatible_observer
892
893
894def validate(*names):
895    """A decorator to register cross validator of HasTraits object's state
896    when a Trait is set.
897
898    The handler passed to the decorator must have one ``proposal`` dict argument.
899    The proposal dictionary must hold the following keys:
900
901    * ``owner`` : the HasTraits instance
902    * ``value`` : the proposed value for the modified trait attribute
903    * ``trait`` : the TraitType instance associated with the attribute
904
905    Parameters
906    ----------
907    *names
908        The str names of the Traits to validate.
909
910    Notes
911    -----
912    Since the owner has access to the ``HasTraits`` instance via the 'owner' key,
913    the registered cross validator could potentially make changes to attributes
914    of the ``HasTraits`` instance. However, we recommend not to do so. The reason
915    is that the cross-validation of attributes may run in arbitrary order when
916    exiting the ``hold_trait_notifications`` context, and such changes may not
917    commute.
918    """
919    if not names:
920        raise TypeError("Please specify at least one trait name to validate.")
921    for name in names:
922        if name is not All and not isinstance(name, str):
923            raise TypeError("trait names to validate must be strings or All, not %r" % name)
924    return ValidateHandler(names)
925
926
927def default(name):
928    """ A decorator which assigns a dynamic default for a Trait on a HasTraits object.
929
930    Parameters
931    ----------
932    name
933        The str name of the Trait on the object whose default should be generated.
934
935    Notes
936    -----
937    Unlike observers and validators which are properties of the HasTraits
938    instance, default value generators are class-level properties.
939
940    Besides, default generators are only invoked if they are registered in
941    subclasses of `this_type`.
942
943    ::
944
945        class A(HasTraits):
946            bar = Int()
947
948            @default('bar')
949            def get_bar_default(self):
950                return 11
951
952        class B(A):
953            bar = Float()  # This trait ignores the default generator defined in
954                           # the base class A
955
956        class C(B):
957
958            @default('bar')
959            def some_other_default(self):  # This default generator should not be
960                return 3.0                 # ignored since it is defined in a
961                                           # class derived from B.a.this_class.
962    """
963    if not isinstance(name, str):
964        raise TypeError("Trait name must be a string or All, not %r" % name)
965    return DefaultHandler(name)
966
967
968class EventHandler(BaseDescriptor):
969
970    def _init_call(self, func):
971        self.func = func
972        return self
973
974    def __call__(self, *args, **kwargs):
975        """Pass `*args` and `**kwargs` to the handler's function if it exists."""
976        if hasattr(self, 'func'):
977            return self.func(*args, **kwargs)
978        else:
979            return self._init_call(*args, **kwargs)
980
981    def __get__(self, inst, cls=None):
982        if inst is None:
983            return self
984        return types.MethodType(self.func, inst)
985
986
987class ObserveHandler(EventHandler):
988
989    def __init__(self, names, type):
990        self.trait_names = names
991        self.type = type
992
993    def instance_init(self, inst):
994        inst.observe(self, self.trait_names, type=self.type)
995
996
997class ValidateHandler(EventHandler):
998
999    def __init__(self, names):
1000        self.trait_names = names
1001
1002    def instance_init(self, inst):
1003        inst._register_validator(self, self.trait_names)
1004
1005
1006class DefaultHandler(EventHandler):
1007
1008    def __init__(self, name):
1009        self.trait_name = name
1010
1011    def class_init(self, cls, name):
1012        super().class_init(cls, name)
1013        cls._trait_default_generators[self.trait_name] = self
1014
1015
1016class HasDescriptors(metaclass=MetaHasDescriptors):
1017    """The base class for all classes that have descriptors.
1018    """
1019
1020    def __new__(*args, **kwargs):
1021        # Pass cls as args[0] to allow "cls" as keyword argument
1022        cls = args[0]
1023        args = args[1:]
1024
1025        # This is needed because object.__new__ only accepts
1026        # the cls argument.
1027        new_meth = super(HasDescriptors, cls).__new__
1028        if new_meth is object.__new__:
1029            inst = new_meth(cls)
1030        else:
1031            inst = new_meth(cls, *args, **kwargs)
1032        inst.setup_instance(*args, **kwargs)
1033        return inst
1034
1035    def setup_instance(*args, **kwargs):
1036        """
1037        This is called **before** self.__init__ is called.
1038        """
1039        # Pass self as args[0] to allow "self" as keyword argument
1040        self = args[0]
1041        args = args[1:]
1042
1043        self._cross_validation_lock = False
1044        cls = self.__class__
1045        for key in dir(cls):
1046            # Some descriptors raise AttributeError like zope.interface's
1047            # __provides__ attributes even though they exist.  This causes
1048            # AttributeErrors even though they are listed in dir(cls).
1049            try:
1050                value = getattr(cls, key)
1051            except AttributeError:
1052                pass
1053            else:
1054                if isinstance(value, BaseDescriptor):
1055                    value.instance_init(self)
1056
1057
1058class HasTraits(HasDescriptors, metaclass=MetaHasTraits):
1059
1060    def setup_instance(*args, **kwargs):
1061        # Pass self as args[0] to allow "self" as keyword argument
1062        self = args[0]
1063        args = args[1:]
1064
1065        self._trait_values = {}
1066        self._trait_notifiers = {}
1067        self._trait_validators = {}
1068        super(HasTraits, self).setup_instance(*args, **kwargs)
1069
1070    def __init__(self, *args, **kwargs):
1071        # Allow trait values to be set using keyword arguments.
1072        # We need to use setattr for this to trigger validation and
1073        # notifications.
1074        super_args = args
1075        super_kwargs = {}
1076        with self.hold_trait_notifications():
1077            for key, value in kwargs.items():
1078                if self.has_trait(key):
1079                    setattr(self, key, value)
1080                else:
1081                    # passthrough args that don't set traits to super
1082                    super_kwargs[key] = value
1083        try:
1084            super(HasTraits, self).__init__(*super_args, **super_kwargs)
1085        except TypeError as e:
1086            arg_s_list = [ repr(arg) for arg in super_args ]
1087            for k, v in super_kwargs.items():
1088                arg_s_list.append("%s=%r" % (k, v))
1089            arg_s = ', '.join(arg_s_list)
1090            warn(
1091                "Passing unrecognized arguments to super({classname}).__init__({arg_s}).\n"
1092                "{error}\n"
1093                "This is deprecated in traitlets 4.2."
1094                "This error will be raised in a future release of traitlets."
1095                .format(
1096                    arg_s=arg_s, classname=self.__class__.__name__,
1097                    error=e,
1098                ),
1099                DeprecationWarning,
1100                stacklevel=2,
1101            )
1102
1103    def __getstate__(self):
1104        d = self.__dict__.copy()
1105        # event handlers stored on an instance are
1106        # expected to be reinstantiated during a
1107        # recall of instance_init during __setstate__
1108        d['_trait_notifiers'] = {}
1109        d['_trait_validators'] = {}
1110        d['_trait_values'] = self._trait_values.copy()
1111        d['_cross_validation_lock'] = False  # FIXME: raise if cloning locked!
1112
1113        return d
1114
1115    def __setstate__(self, state):
1116        self.__dict__ = state.copy()
1117
1118        # event handlers are reassigned to self
1119        cls = self.__class__
1120        for key in dir(cls):
1121            # Some descriptors raise AttributeError like zope.interface's
1122            # __provides__ attributes even though they exist.  This causes
1123            # AttributeErrors even though they are listed in dir(cls).
1124            try:
1125                value = getattr(cls, key)
1126            except AttributeError:
1127                pass
1128            else:
1129                if isinstance(value, EventHandler):
1130                    value.instance_init(self)
1131
1132    @property
1133    @contextlib.contextmanager
1134    def cross_validation_lock(self):
1135        """
1136        A contextmanager for running a block with our cross validation lock set
1137        to True.
1138
1139        At the end of the block, the lock's value is restored to its value
1140        prior to entering the block.
1141        """
1142        if self._cross_validation_lock:
1143            yield
1144            return
1145        else:
1146            try:
1147                self._cross_validation_lock = True
1148                yield
1149            finally:
1150                self._cross_validation_lock = False
1151
1152    @contextlib.contextmanager
1153    def hold_trait_notifications(self):
1154        """Context manager for bundling trait change notifications and cross
1155        validation.
1156
1157        Use this when doing multiple trait assignments (init, config), to avoid
1158        race conditions in trait notifiers requesting other trait values.
1159        All trait notifications will fire after all values have been assigned.
1160        """
1161        if self._cross_validation_lock:
1162            yield
1163            return
1164        else:
1165            cache = {}
1166            notify_change = self.notify_change
1167
1168            def compress(past_changes, change):
1169                """Merges the provided change with the last if possible."""
1170                if past_changes is None:
1171                    return [change]
1172                else:
1173                    if past_changes[-1]['type'] == 'change' and change.type == 'change':
1174                        past_changes[-1]['new'] = change.new
1175                    else:
1176                        # In case of changes other than 'change', append the notification.
1177                        past_changes.append(change)
1178                    return past_changes
1179
1180            def hold(change):
1181                name = change.name
1182                cache[name] = compress(cache.get(name), change)
1183
1184            try:
1185                # Replace notify_change with `hold`, caching and compressing
1186                # notifications, disable cross validation and yield.
1187                self.notify_change = hold
1188                self._cross_validation_lock = True
1189                yield
1190                # Cross validate final values when context is released.
1191                for name in list(cache.keys()):
1192                    trait = getattr(self.__class__, name)
1193                    value = trait._cross_validate(self, getattr(self, name))
1194                    self.set_trait(name, value)
1195            except TraitError as e:
1196                # Roll back in case of TraitError during final cross validation.
1197                self.notify_change = lambda x: None
1198                for name, changes in cache.items():
1199                    for change in changes[::-1]:
1200                        # TODO: Separate in a rollback function per notification type.
1201                        if change.type == 'change':
1202                            if change.old is not Undefined:
1203                                self.set_trait(name, change.old)
1204                            else:
1205                                self._trait_values.pop(name)
1206                cache = {}
1207                raise e
1208            finally:
1209                self._cross_validation_lock = False
1210                # Restore method retrieval from class
1211                del self.notify_change
1212
1213                # trigger delayed notifications
1214                for changes in cache.values():
1215                    for change in changes:
1216                        self.notify_change(change)
1217
1218    def _notify_trait(self, name, old_value, new_value):
1219        self.notify_change(Bunch(
1220            name=name,
1221            old=old_value,
1222            new=new_value,
1223            owner=self,
1224            type='change',
1225        ))
1226
1227    def notify_change(self, change):
1228        """Notify observers of a change event"""
1229        return self._notify_observers(change)
1230
1231    def _notify_observers(self, event):
1232        """Notify observers of any event"""
1233        if not isinstance(event, Bunch):
1234            # cast to bunch if given a dict
1235            event = Bunch(event)
1236        name, type = event.name, event.type
1237
1238        callables = []
1239        callables.extend(self._trait_notifiers.get(name, {}).get(type, []))
1240        callables.extend(self._trait_notifiers.get(name, {}).get(All, []))
1241        callables.extend(self._trait_notifiers.get(All, {}).get(type, []))
1242        callables.extend(self._trait_notifiers.get(All, {}).get(All, []))
1243
1244        # Now static ones
1245        magic_name = '_%s_changed' % name
1246        if event.type == "change" and hasattr(self, magic_name):
1247            class_value = getattr(self.__class__, magic_name)
1248            if not isinstance(class_value, ObserveHandler):
1249                _deprecated_method(class_value, self.__class__, magic_name,
1250                    "use @observe and @unobserve instead.")
1251                cb = getattr(self, magic_name)
1252                # Only append the magic method if it was not manually registered
1253                if cb not in callables:
1254                    callables.append(_callback_wrapper(cb))
1255
1256        # Call them all now
1257        # Traits catches and logs errors here.  I allow them to raise
1258        for c in callables:
1259            # Bound methods have an additional 'self' argument.
1260
1261            if isinstance(c, _CallbackWrapper):
1262                c = c.__call__
1263            elif isinstance(c, EventHandler) and c.name is not None:
1264                c = getattr(self, c.name)
1265
1266            c(event)
1267
1268    def _add_notifiers(self, handler, name, type):
1269        if name not in self._trait_notifiers:
1270            nlist = []
1271            self._trait_notifiers[name] = {type: nlist}
1272        else:
1273            if type not in self._trait_notifiers[name]:
1274                nlist = []
1275                self._trait_notifiers[name][type] = nlist
1276            else:
1277                nlist = self._trait_notifiers[name][type]
1278        if handler not in nlist:
1279            nlist.append(handler)
1280
1281    def _remove_notifiers(self, handler, name, type):
1282        try:
1283            if handler is None:
1284                del self._trait_notifiers[name][type]
1285            else:
1286                self._trait_notifiers[name][type].remove(handler)
1287        except KeyError:
1288            pass
1289
1290    def on_trait_change(self, handler=None, name=None, remove=False):
1291        """DEPRECATED: Setup a handler to be called when a trait changes.
1292
1293        This is used to setup dynamic notifications of trait changes.
1294
1295        Static handlers can be created by creating methods on a HasTraits
1296        subclass with the naming convention '_[traitname]_changed'.  Thus,
1297        to create static handler for the trait 'a', create the method
1298        _a_changed(self, name, old, new) (fewer arguments can be used, see
1299        below).
1300
1301        If `remove` is True and `handler` is not specified, all change
1302        handlers for the specified name are uninstalled.
1303
1304        Parameters
1305        ----------
1306        handler : callable, None
1307            A callable that is called when a trait changes.  Its
1308            signature can be handler(), handler(name), handler(name, new),
1309            handler(name, old, new), or handler(name, old, new, self).
1310        name : list, str, None
1311            If None, the handler will apply to all traits.  If a list
1312            of str, handler will apply to all names in the list.  If a
1313            str, the handler will apply just to that name.
1314        remove : bool
1315            If False (the default), then install the handler.  If True
1316            then unintall it.
1317        """
1318        warn("on_trait_change is deprecated in traitlets 4.1: use observe instead",
1319             DeprecationWarning, stacklevel=2)
1320        if name is None:
1321            name = All
1322        if remove:
1323            self.unobserve(_callback_wrapper(handler), names=name)
1324        else:
1325            self.observe(_callback_wrapper(handler), names=name)
1326
1327    def observe(self, handler, names=All, type='change'):
1328        """Setup a handler to be called when a trait changes.
1329
1330        This is used to setup dynamic notifications of trait changes.
1331
1332        Parameters
1333        ----------
1334        handler : callable
1335            A callable that is called when a trait changes. Its
1336            signature should be ``handler(change)``, where ``change`` is a
1337            dictionary. The change dictionary at least holds a 'type' key.
1338            * ``type``: the type of notification.
1339            Other keys may be passed depending on the value of 'type'. In the
1340            case where type is 'change', we also have the following keys:
1341            * ``owner`` : the HasTraits instance
1342            * ``old`` : the old value of the modified trait attribute
1343            * ``new`` : the new value of the modified trait attribute
1344            * ``name`` : the name of the modified trait attribute.
1345        names : list, str, All
1346            If names is All, the handler will apply to all traits.  If a list
1347            of str, handler will apply to all names in the list.  If a
1348            str, the handler will apply just to that name.
1349        type : str, All (default: 'change')
1350            The type of notification to filter by. If equal to All, then all
1351            notifications are passed to the observe handler.
1352        """
1353        names = parse_notifier_name(names)
1354        for n in names:
1355            self._add_notifiers(handler, n, type)
1356
1357    def unobserve(self, handler, names=All, type='change'):
1358        """Remove a trait change handler.
1359
1360        This is used to unregister handlers to trait change notifications.
1361
1362        Parameters
1363        ----------
1364        handler : callable
1365            The callable called when a trait attribute changes.
1366        names : list, str, All (default: All)
1367            The names of the traits for which the specified handler should be
1368            uninstalled. If names is All, the specified handler is uninstalled
1369            from the list of notifiers corresponding to all changes.
1370        type : str or All (default: 'change')
1371            The type of notification to filter by. If All, the specified handler
1372            is uninstalled from the list of notifiers corresponding to all types.
1373        """
1374        names = parse_notifier_name(names)
1375        for n in names:
1376            self._remove_notifiers(handler, n, type)
1377
1378    def unobserve_all(self, name=All):
1379        """Remove trait change handlers of any type for the specified name.
1380        If name is not specified, removes all trait notifiers."""
1381        if name is All:
1382            self._trait_notifiers = {}
1383        else:
1384            try:
1385                del self._trait_notifiers[name]
1386            except KeyError:
1387                pass
1388
1389    def _register_validator(self, handler, names):
1390        """Setup a handler to be called when a trait should be cross validated.
1391
1392        This is used to setup dynamic notifications for cross-validation.
1393
1394        If a validator is already registered for any of the provided names, a
1395        TraitError is raised and no new validator is registered.
1396
1397        Parameters
1398        ----------
1399        handler : callable
1400            A callable that is called when the given trait is cross-validated.
1401            Its signature is handler(proposal), where proposal is a Bunch (dictionary with attribute access)
1402            with the following attributes/keys:
1403                * ``owner`` : the HasTraits instance
1404                * ``value`` : the proposed value for the modified trait attribute
1405                * ``trait`` : the TraitType instance associated with the attribute
1406        names : List of strings
1407            The names of the traits that should be cross-validated
1408        """
1409        for name in names:
1410            magic_name = '_%s_validate' % name
1411            if hasattr(self, magic_name):
1412                class_value = getattr(self.__class__, magic_name)
1413                if not isinstance(class_value, ValidateHandler):
1414                    _deprecated_method(class_value, self.__class__, magic_name,
1415                        "use @validate decorator instead.")
1416        for name in names:
1417            self._trait_validators[name] = handler
1418
1419    def add_traits(self, **traits):
1420        """Dynamically add trait attributes to the HasTraits instance."""
1421        cls = self.__class__
1422        attrs = {"__module__": cls.__module__}
1423        if hasattr(cls, "__qualname__"):
1424          # __qualname__ introduced in Python 3.3 (see PEP 3155)
1425          attrs["__qualname__"] = cls.__qualname__
1426        attrs.update(traits)
1427        self.__class__ = type(cls.__name__, (cls,), attrs)
1428        for trait in traits.values():
1429            trait.instance_init(self)
1430
1431    def set_trait(self, name, value):
1432        """Forcibly sets trait attribute, including read-only attributes."""
1433        cls = self.__class__
1434        if not self.has_trait(name):
1435            raise TraitError("Class %s does not have a trait named %s" %
1436                                (cls.__name__, name))
1437        else:
1438            getattr(cls, name).set(self, value)
1439
1440    @classmethod
1441    def class_trait_names(cls, **metadata):
1442        """Get a list of all the names of this class' traits.
1443
1444        This method is just like the :meth:`trait_names` method,
1445        but is unbound.
1446        """
1447        return list(cls.class_traits(**metadata))
1448
1449    @classmethod
1450    def class_traits(cls, **metadata):
1451        """Get a ``dict`` of all the traits of this class.  The dictionary
1452        is keyed on the name and the values are the TraitType objects.
1453
1454        This method is just like the :meth:`traits` method, but is unbound.
1455
1456        The TraitTypes returned don't know anything about the values
1457        that the various HasTrait's instances are holding.
1458
1459        The metadata kwargs allow functions to be passed in which
1460        filter traits based on metadata values.  The functions should
1461        take a single value as an argument and return a boolean.  If
1462        any function returns False, then the trait is not included in
1463        the output.  If a metadata key doesn't exist, None will be passed
1464        to the function.
1465        """
1466        traits = dict([memb for memb in getmembers(cls) if
1467                     isinstance(memb[1], TraitType)])
1468
1469        if len(metadata) == 0:
1470            return traits
1471
1472        result = {}
1473        for name, trait in traits.items():
1474            for meta_name, meta_eval in metadata.items():
1475                if not callable(meta_eval):
1476                    meta_eval = _SimpleTest(meta_eval)
1477                if not meta_eval(trait.metadata.get(meta_name, None)):
1478                    break
1479            else:
1480                result[name] = trait
1481
1482        return result
1483
1484    @classmethod
1485    def class_own_traits(cls, **metadata):
1486        """Get a dict of all the traitlets defined on this class, not a parent.
1487
1488        Works like `class_traits`, except for excluding traits from parents.
1489        """
1490        sup = super(cls, cls)
1491        return {n: t for (n, t) in cls.class_traits(**metadata).items()
1492                if getattr(sup, n, None) is not t}
1493
1494    def has_trait(self, name):
1495        """Returns True if the object has a trait with the specified name."""
1496        return isinstance(getattr(self.__class__, name, None), TraitType)
1497
1498    def trait_has_value(self, name):
1499        """Returns True if the specified trait has a value.
1500
1501        This will return false even if ``getattr`` would return a
1502        dynamically generated default value. These default values
1503        will be recognized as existing only after they have been
1504        generated.
1505
1506        Example
1507
1508        .. code-block:: python
1509
1510            class MyClass(HasTraits):
1511                i = Int()
1512
1513            mc = MyClass()
1514            assert not mc.trait_has_value("i")
1515            mc.i # generates a default value
1516            assert mc.trait_has_value("i")
1517        """
1518        return name in self._trait_values
1519
1520    def trait_values(self, **metadata):
1521        """A ``dict`` of trait names and their values.
1522
1523        The metadata kwargs allow functions to be passed in which
1524        filter traits based on metadata values.  The functions should
1525        take a single value as an argument and return a boolean.  If
1526        any function returns False, then the trait is not included in
1527        the output.  If a metadata key doesn't exist, None will be passed
1528        to the function.
1529
1530        Returns
1531        -------
1532        A ``dict`` of trait names and their values.
1533
1534        Notes
1535        -----
1536        Trait values are retrieved via ``getattr``, any exceptions raised
1537        by traits or the operations they may trigger will result in the
1538        absence of a trait value in the result ``dict``.
1539        """
1540        return {name: getattr(self, name) for name in self.trait_names(**metadata)}
1541
1542    def _get_trait_default_generator(self, name):
1543        """Return default generator for a given trait
1544
1545        Walk the MRO to resolve the correct default generator according to inheritance.
1546        """
1547        method_name = '_%s_default' % name
1548        if method_name in self.__dict__:
1549            return getattr(self, method_name)
1550        cls = self.__class__
1551        trait = getattr(cls, name)
1552        assert isinstance(trait, TraitType)
1553        # truncate mro to the class on which the trait is defined
1554        mro = cls.mro()
1555        try:
1556            mro = mro[:mro.index(trait.this_class) + 1]
1557        except ValueError:
1558            # this_class not in mro
1559            pass
1560        for c in mro:
1561            if method_name in c.__dict__:
1562                return getattr(c, method_name)
1563            if name in c.__dict__.get('_trait_default_generators', {}):
1564                return c._trait_default_generators[name]
1565        return trait.default
1566
1567    def trait_defaults(self, *names, **metadata):
1568        """Return a trait's default value or a dictionary of them
1569
1570        Notes
1571        -----
1572        Dynamically generated default values may
1573        depend on the current state of the object."""
1574        for n in names:
1575            if not self.has_trait(n):
1576                raise TraitError("'%s' is not a trait of '%s' "
1577                    "instances" % (n, type(self).__name__))
1578
1579        if len(names) == 1 and len(metadata) == 0:
1580            return self._get_trait_default_generator(names[0])(self)
1581
1582        trait_names = self.trait_names(**metadata)
1583        trait_names.extend(names)
1584
1585        defaults = {}
1586        for n in trait_names:
1587            defaults[n] = self._get_trait_default_generator(n)(self)
1588        return defaults
1589
1590    def trait_names(self, **metadata):
1591        """Get a list of all the names of this class' traits."""
1592        return list(self.traits(**metadata))
1593
1594    def traits(self, **metadata):
1595        """Get a ``dict`` of all the traits of this class.  The dictionary
1596        is keyed on the name and the values are the TraitType objects.
1597
1598        The TraitTypes returned don't know anything about the values
1599        that the various HasTrait's instances are holding.
1600
1601        The metadata kwargs allow functions to be passed in which
1602        filter traits based on metadata values.  The functions should
1603        take a single value as an argument and return a boolean.  If
1604        any function returns False, then the trait is not included in
1605        the output.  If a metadata key doesn't exist, None will be passed
1606        to the function.
1607        """
1608        traits = dict([memb for memb in getmembers(self.__class__) if
1609                     isinstance(memb[1], TraitType)])
1610
1611        if len(metadata) == 0:
1612            return traits
1613
1614        result = {}
1615        for name, trait in traits.items():
1616            for meta_name, meta_eval in metadata.items():
1617                if not callable(meta_eval):
1618                    meta_eval = _SimpleTest(meta_eval)
1619                if not meta_eval(trait.metadata.get(meta_name, None)):
1620                    break
1621            else:
1622                result[name] = trait
1623
1624        return result
1625
1626    def trait_metadata(self, traitname, key, default=None):
1627        """Get metadata values for trait by key."""
1628        try:
1629            trait = getattr(self.__class__, traitname)
1630        except AttributeError:
1631            raise TraitError("Class %s does not have a trait named %s" %
1632                                (self.__class__.__name__, traitname))
1633        metadata_name = '_' + traitname + '_metadata'
1634        if hasattr(self, metadata_name) and key in getattr(self, metadata_name):
1635            return getattr(self, metadata_name).get(key, default)
1636        else:
1637            return trait.metadata.get(key, default)
1638
1639    @classmethod
1640    def class_own_trait_events(cls, name):
1641        """Get a dict of all event handlers defined on this class, not a parent.
1642
1643        Works like ``event_handlers``, except for excluding traits from parents.
1644        """
1645        sup = super(cls, cls)
1646        return {n: e for (n, e) in cls.events(name).items()
1647                if getattr(sup, n, None) is not e}
1648
1649    @classmethod
1650    def trait_events(cls, name=None):
1651        """Get a ``dict`` of all the event handlers of this class.
1652
1653        Parameters
1654        ----------
1655        name : str (default: None)
1656            The name of a trait of this class. If name is ``None`` then all
1657            the event handlers of this class will be returned instead.
1658
1659        Returns
1660        -------
1661        The event handlers associated with a trait name, or all event handlers.
1662        """
1663        events = {}
1664        for k, v in getmembers(cls):
1665            if isinstance(v, EventHandler):
1666                if name is None:
1667                    events[k] = v
1668                elif name in v.trait_names:
1669                    events[k] = v
1670                elif hasattr(v, 'tags'):
1671                    if cls.trait_names(**v.tags):
1672                        events[k] = v
1673        return events
1674
1675#-----------------------------------------------------------------------------
1676# Actual TraitTypes implementations/subclasses
1677#-----------------------------------------------------------------------------
1678
1679#-----------------------------------------------------------------------------
1680# TraitTypes subclasses for handling classes and instances of classes
1681#-----------------------------------------------------------------------------
1682
1683
1684class ClassBasedTraitType(TraitType):
1685    """
1686    A trait with error reporting and string -> type resolution for Type,
1687    Instance and This.
1688    """
1689
1690    def _resolve_string(self, string):
1691        """
1692        Resolve a string supplied for a type into an actual object.
1693        """
1694        return import_item(string)
1695
1696
1697class Type(ClassBasedTraitType):
1698    """A trait whose value must be a subclass of a specified class."""
1699
1700    def __init__(self, default_value=Undefined, klass=None, **kwargs):
1701        """Construct a Type trait
1702
1703        A Type trait specifies that its values must be subclasses of
1704        a particular class.
1705
1706        If only ``default_value`` is given, it is used for the ``klass`` as
1707        well. If neither are given, both default to ``object``.
1708
1709        Parameters
1710        ----------
1711        default_value : class, str or None
1712            The default value must be a subclass of klass.  If an str,
1713            the str must be a fully specified class name, like 'foo.bar.Bah'.
1714            The string is resolved into real class, when the parent
1715            :class:`HasTraits` class is instantiated.
1716        klass : class, str [ default object ]
1717            Values of this trait must be a subclass of klass.  The klass
1718            may be specified in a string like: 'foo.bar.MyClass'.
1719            The string is resolved into real class, when the parent
1720            :class:`HasTraits` class is instantiated.
1721        allow_none : bool [ default False ]
1722            Indicates whether None is allowed as an assignable value.
1723        **kwargs
1724            extra kwargs passed to `ClassBasedTraitType`
1725        """
1726        if default_value is Undefined:
1727            new_default_value = object if (klass is None) else klass
1728        else:
1729            new_default_value = default_value
1730
1731        if klass is None:
1732            if (default_value is None) or (default_value is Undefined):
1733                klass = object
1734            else:
1735                klass = default_value
1736
1737        if not (inspect.isclass(klass) or isinstance(klass, str)):
1738            raise TraitError("A Type trait must specify a class.")
1739
1740        self.klass = klass
1741
1742        super().__init__(new_default_value, **kwargs)
1743
1744    def validate(self, obj, value):
1745        """Validates that the value is a valid object instance."""
1746        if isinstance(value, str):
1747            try:
1748                value = self._resolve_string(value)
1749            except ImportError:
1750                raise TraitError("The '%s' trait of %s instance must be a type, but "
1751                                 "%r could not be imported" % (self.name, obj, value))
1752        try:
1753            if issubclass(value, self.klass):
1754                return value
1755        except Exception:
1756            pass
1757
1758        self.error(obj, value)
1759
1760    def info(self):
1761        """ Returns a description of the trait."""
1762        if isinstance(self.klass, str):
1763            klass = self.klass
1764        else:
1765            klass = self.klass.__module__ + '.' + self.klass.__name__
1766        result = "a subclass of '%s'" % klass
1767        if self.allow_none:
1768            return result + ' or None'
1769        return result
1770
1771    def instance_init(self, obj):
1772        self._resolve_classes()
1773        super().instance_init(obj)
1774
1775    def _resolve_classes(self):
1776        if isinstance(self.klass, str):
1777            self.klass = self._resolve_string(self.klass)
1778        if isinstance(self.default_value, str):
1779            self.default_value = self._resolve_string(self.default_value)
1780
1781    def default_value_repr(self):
1782        value = self.default_value
1783        if isinstance(value, str):
1784            return repr(value)
1785        else:
1786            return repr(f'{value.__module__}.{value.__name__}')
1787
1788
1789class Instance(ClassBasedTraitType):
1790    """A trait whose value must be an instance of a specified class.
1791
1792    The value can also be an instance of a subclass of the specified class.
1793
1794    Subclasses can declare default classes by overriding the klass attribute
1795    """
1796
1797    klass = None
1798
1799    def __init__(self, klass=None, args=None, kw=None, **kwargs):
1800        """Construct an Instance trait.
1801
1802        This trait allows values that are instances of a particular
1803        class or its subclasses.  Our implementation is quite different
1804        from that of enthough.traits as we don't allow instances to be used
1805        for klass and we handle the ``args`` and ``kw`` arguments differently.
1806
1807        Parameters
1808        ----------
1809        klass : class, str
1810            The class that forms the basis for the trait.  Class names
1811            can also be specified as strings, like 'foo.bar.Bar'.
1812        args : tuple
1813            Positional arguments for generating the default value.
1814        kw : dict
1815            Keyword arguments for generating the default value.
1816        allow_none : bool [ default False ]
1817            Indicates whether None is allowed as a value.
1818        **kwargs
1819            Extra kwargs passed to `ClassBasedTraitType`
1820
1821        Notes
1822        -----
1823        If both ``args`` and ``kw`` are None, then the default value is None.
1824        If ``args`` is a tuple and ``kw`` is a dict, then the default is
1825        created as ``klass(*args, **kw)``.  If exactly one of ``args`` or ``kw`` is
1826        None, the None is replaced by ``()`` or ``{}``, respectively.
1827        """
1828        if klass is None:
1829            klass = self.klass
1830
1831        if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, str)):
1832            self.klass = klass
1833        else:
1834            raise TraitError('The klass attribute must be a class'
1835                                ' not: %r' % klass)
1836
1837        if (kw is not None) and not isinstance(kw, dict):
1838            raise TraitError("The 'kw' argument must be a dict or None.")
1839        if (args is not None) and not isinstance(args, tuple):
1840            raise TraitError("The 'args' argument must be a tuple or None.")
1841
1842        self.default_args = args
1843        self.default_kwargs = kw
1844
1845        super(Instance, self).__init__(**kwargs)
1846
1847    def validate(self, obj, value):
1848        if isinstance(value, self.klass):
1849            return value
1850        else:
1851            self.error(obj, value)
1852
1853    def info(self):
1854        if isinstance(self.klass, str):
1855            result = add_article(self.klass)
1856        else:
1857            result = describe("a", self.klass)
1858        if self.allow_none:
1859            result += ' or None'
1860        return result
1861
1862    def instance_init(self, obj):
1863        self._resolve_classes()
1864        super().instance_init(obj)
1865
1866    def _resolve_classes(self):
1867        if isinstance(self.klass, str):
1868            self.klass = self._resolve_string(self.klass)
1869
1870    def make_dynamic_default(self):
1871        if (self.default_args is None) and (self.default_kwargs is None):
1872            return None
1873        return self.klass(*(self.default_args or ()),
1874                          **(self.default_kwargs or {}))
1875
1876    def default_value_repr(self):
1877        return repr(self.make_dynamic_default())
1878
1879    def from_string(self, s):
1880        return _safe_literal_eval(s)
1881
1882
1883class ForwardDeclaredMixin(object):
1884    """
1885    Mixin for forward-declared versions of Instance and Type.
1886    """
1887    def _resolve_string(self, string):
1888        """
1889        Find the specified class name by looking for it in the module in which
1890        our this_class attribute was defined.
1891        """
1892        modname = self.this_class.__module__
1893        return import_item('.'.join([modname, string]))
1894
1895
1896class ForwardDeclaredType(ForwardDeclaredMixin, Type):
1897    """
1898    Forward-declared version of Type.
1899    """
1900    pass
1901
1902
1903class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
1904    """
1905    Forward-declared version of Instance.
1906    """
1907    pass
1908
1909
1910class This(ClassBasedTraitType):
1911    """A trait for instances of the class containing this trait.
1912
1913    Because how how and when class bodies are executed, the ``This``
1914    trait can only have a default value of None.  This, and because we
1915    always validate default values, ``allow_none`` is *always* true.
1916    """
1917
1918    info_text = 'an instance of the same type as the receiver or None'
1919
1920    def __init__(self, **kwargs):
1921        super(This, self).__init__(None, **kwargs)
1922
1923    def validate(self, obj, value):
1924        # What if value is a superclass of obj.__class__?  This is
1925        # complicated if it was the superclass that defined the This
1926        # trait.
1927        if isinstance(value, self.this_class) or (value is None):
1928            return value
1929        else:
1930            self.error(obj, value)
1931
1932
1933class Union(TraitType):
1934    """A trait type representing a Union type."""
1935
1936    def __init__(self, trait_types, **kwargs):
1937        """Construct a Union  trait.
1938
1939        This trait allows values that are allowed by at least one of the
1940        specified trait types. A Union traitlet cannot have metadata on
1941        its own, besides the metadata of the listed types.
1942
1943        Parameters
1944        ----------
1945        trait_types : sequence
1946            The list of trait types of length at least 1.
1947
1948        Notes
1949        -----
1950        Union([Float(), Bool(), Int()]) attempts to validate the provided values
1951        with the validation function of Float, then Bool, and finally Int.
1952        """
1953        self.trait_types = list(trait_types)
1954        self.info_text = " or ".join([tt.info() for tt in self.trait_types])
1955        super(Union, self).__init__(**kwargs)
1956
1957    def default(self, obj=None):
1958        default = super(Union, self).default(obj)
1959        for t in self.trait_types:
1960            if default is Undefined:
1961                default = t.default(obj)
1962            else:
1963                break
1964        return default
1965
1966    def class_init(self, cls, name):
1967        for trait_type in reversed(self.trait_types):
1968            trait_type.class_init(cls, None)
1969        super(Union, self).class_init(cls, name)
1970
1971    def instance_init(self, obj):
1972        for trait_type in reversed(self.trait_types):
1973            trait_type.instance_init(obj)
1974        super(Union, self).instance_init(obj)
1975
1976    def validate(self, obj, value):
1977        with obj.cross_validation_lock:
1978            for trait_type in self.trait_types:
1979                try:
1980                    v = trait_type._validate(obj, value)
1981                    # In the case of an element trait, the name is None
1982                    if self.name is not None:
1983                        setattr(obj, '_' + self.name + '_metadata', trait_type.metadata)
1984                    return v
1985                except TraitError:
1986                    continue
1987        self.error(obj, value)
1988
1989    def __or__(self, other):
1990        if isinstance(other, Union):
1991            return Union(self.trait_types + other.trait_types)
1992        else:
1993            return Union(self.trait_types + [other])
1994
1995
1996#-----------------------------------------------------------------------------
1997# Basic TraitTypes implementations/subclasses
1998#-----------------------------------------------------------------------------
1999
2000
2001class Any(TraitType):
2002    """A trait which allows any value."""
2003    default_value = None
2004    allow_none = True
2005    info_text = 'any value'
2006
2007
2008def _validate_bounds(trait, obj, value):
2009    """
2010    Validate that a number to be applied to a trait is between bounds.
2011
2012    If value is not between min_bound and max_bound, this raises a
2013    TraitError with an error message appropriate for this trait.
2014    """
2015    if trait.min is not None and value < trait.min:
2016        raise TraitError(
2017            "The value of the '{name}' trait of {klass} instance should "
2018            "not be less than {min_bound}, but a value of {value} was "
2019            "specified".format(
2020                name=trait.name, klass=class_of(obj),
2021                value=value, min_bound=trait.min))
2022    if trait.max is not None and value > trait.max:
2023        raise TraitError(
2024            "The value of the '{name}' trait of {klass} instance should "
2025            "not be greater than {max_bound}, but a value of {value} was "
2026            "specified".format(
2027                name=trait.name, klass=class_of(obj),
2028                value=value, max_bound=trait.max))
2029    return value
2030
2031
2032class Int(TraitType):
2033    """An int trait."""
2034
2035    default_value = 0
2036    info_text = 'an int'
2037
2038    def __init__(self, default_value=Undefined, allow_none=False, **kwargs):
2039        self.min = kwargs.pop('min', None)
2040        self.max = kwargs.pop('max', None)
2041        super(Int, self).__init__(default_value=default_value,
2042                                  allow_none=allow_none, **kwargs)
2043
2044    def validate(self, obj, value):
2045        if not isinstance(value, int):
2046            self.error(obj, value)
2047        return _validate_bounds(self, obj, value)
2048
2049    def from_string(self, s):
2050        if self.allow_none and s == 'None':
2051            return None
2052        return int(s)
2053
2054
2055class CInt(Int):
2056    """A casting version of the int trait."""
2057
2058    def validate(self, obj, value):
2059        try:
2060            value = int(value)
2061        except Exception:
2062            self.error(obj, value)
2063        return _validate_bounds(self, obj, value)
2064
2065
2066Long, CLong = Int, CInt
2067Integer = Int
2068
2069
2070class Float(TraitType):
2071    """A float trait."""
2072
2073    default_value = 0.0
2074    info_text = 'a float'
2075
2076    def __init__(self, default_value=Undefined, allow_none=False, **kwargs):
2077        self.min = kwargs.pop('min', -float('inf'))
2078        self.max = kwargs.pop('max', float('inf'))
2079        super(Float, self).__init__(default_value=default_value,
2080                                    allow_none=allow_none, **kwargs)
2081
2082    def validate(self, obj, value):
2083        if isinstance(value, int):
2084            value = float(value)
2085        if not isinstance(value, float):
2086            self.error(obj, value)
2087        return _validate_bounds(self, obj, value)
2088
2089    def from_string(self, s):
2090        if self.allow_none and s == 'None':
2091            return None
2092        return float(s)
2093
2094
2095class CFloat(Float):
2096    """A casting version of the float trait."""
2097
2098    def validate(self, obj, value):
2099        try:
2100            value = float(value)
2101        except Exception:
2102            self.error(obj, value)
2103        return _validate_bounds(self, obj, value)
2104
2105
2106class Complex(TraitType):
2107    """A trait for complex numbers."""
2108
2109    default_value = 0.0 + 0.0j
2110    info_text = 'a complex number'
2111
2112    def validate(self, obj, value):
2113        if isinstance(value, complex):
2114            return value
2115        if isinstance(value, (float, int)):
2116            return complex(value)
2117        self.error(obj, value)
2118
2119    def from_string(self, s):
2120        if self.allow_none and s == 'None':
2121            return None
2122        return complex(s)
2123
2124
2125class CComplex(Complex):
2126    """A casting version of the complex number trait."""
2127
2128    def validate (self, obj, value):
2129        try:
2130            return complex(value)
2131        except Exception:
2132            self.error(obj, value)
2133
2134# We should always be explicit about whether we're using bytes or unicode, both
2135# for Python 3 conversion and for reliable unicode behaviour on Python 2. So
2136# we don't have a Str type.
2137class Bytes(TraitType):
2138    """A trait for byte strings."""
2139
2140    default_value = b''
2141    info_text = 'a bytes object'
2142
2143    def validate(self, obj, value):
2144        if isinstance(value, bytes):
2145            return value
2146        self.error(obj, value)
2147
2148    def from_string(self, s):
2149        if self.allow_none and s == "None":
2150            return None
2151        if len(s) >= 3:
2152            # handle deprecated b"string"
2153            for quote in ('"', "'"):
2154                if s[:2] == f"b{quote}" and s[-1] == quote:
2155                    old_s = s
2156                    s = s[2:-1]
2157                    warn(
2158                        "Supporting extra quotes around Bytes is deprecated in traitlets 5.0. "
2159                        "Use %r instead of %r." % (s, old_s),
2160                        FutureWarning)
2161                    break
2162        return s.encode("utf8")
2163
2164
2165class CBytes(Bytes):
2166    """A casting version of the byte string trait."""
2167
2168    def validate(self, obj, value):
2169        try:
2170            return bytes(value)
2171        except Exception:
2172            self.error(obj, value)
2173
2174
2175class Unicode(TraitType):
2176    """A trait for unicode strings."""
2177
2178    default_value = ''
2179    info_text = 'a unicode string'
2180
2181    def validate(self, obj, value):
2182        if isinstance(value, str):
2183            return value
2184        if isinstance(value, bytes):
2185            try:
2186                return value.decode('ascii', 'strict')
2187            except UnicodeDecodeError:
2188                msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
2189                raise TraitError(msg.format(value, self.name, class_of(obj)))
2190        self.error(obj, value)
2191
2192    def from_string(self, s):
2193        if self.allow_none and s == 'None':
2194            return None
2195        s = os.path.expanduser(s)
2196        if len(s) >= 2:
2197            # handle deprecated "1"
2198            for c in ('"', "'"):
2199                if s[0] == s[-1] == c:
2200                    old_s = s
2201                    s = s[1:-1]
2202                    warn(
2203                        "Supporting extra quotes around strings is deprecated in traitlets 5.0. "
2204                        "You can use %r instead of %r if you require traitlets >=5." % (s, old_s),
2205                        FutureWarning)
2206        return s
2207
2208
2209
2210class CUnicode(Unicode):
2211    """A casting version of the unicode trait."""
2212
2213    def validate(self, obj, value):
2214        try:
2215            return str(value)
2216        except Exception:
2217            self.error(obj, value)
2218
2219
2220class ObjectName(TraitType):
2221    """A string holding a valid object name in this version of Python.
2222
2223    This does not check that the name exists in any scope."""
2224    info_text = "a valid object identifier in Python"
2225
2226    coerce_str = staticmethod(lambda _,s: s)
2227
2228    def validate(self, obj, value):
2229        value = self.coerce_str(obj, value)
2230
2231        if isinstance(value, str) and isidentifier(value):
2232            return value
2233        self.error(obj, value)
2234
2235    def from_string(self, s):
2236        if self.allow_none and s == 'None':
2237            return None
2238        return s
2239
2240class DottedObjectName(ObjectName):
2241    """A string holding a valid dotted object name in Python, such as A.b3._c"""
2242    def validate(self, obj, value):
2243        value = self.coerce_str(obj, value)
2244
2245        if isinstance(value, str) and all(isidentifier(a)
2246          for a in value.split('.')):
2247            return value
2248        self.error(obj, value)
2249
2250
2251class Bool(TraitType):
2252    """A boolean (True, False) trait."""
2253
2254    default_value = False
2255    info_text = 'a boolean'
2256
2257    def validate(self, obj, value):
2258        if isinstance(value, bool):
2259            return value
2260        elif isinstance(value, int):
2261            if value == 1:
2262                return True
2263            elif value == 0:
2264                return False
2265        self.error(obj, value)
2266
2267    def from_string(self, s):
2268        if self.allow_none and s == 'None':
2269            return None
2270        s = s.lower()
2271        if s in {'true', '1'}:
2272            return True
2273        elif s in {'false', '0'}:
2274            return False
2275        else:
2276            raise ValueError("%r is not 1, 0, true, or false")
2277
2278
2279class CBool(Bool):
2280    """A casting version of the boolean trait."""
2281
2282    def validate(self, obj, value):
2283        try:
2284            return bool(value)
2285        except Exception:
2286            self.error(obj, value)
2287
2288
2289class Enum(TraitType):
2290    """An enum whose value must be in a given sequence."""
2291
2292    def __init__(self, values, default_value=Undefined, **kwargs):
2293        self.values = values
2294        if kwargs.get('allow_none', False) and default_value is Undefined:
2295            default_value = None
2296        super(Enum, self).__init__(default_value, **kwargs)
2297
2298    def validate(self, obj, value):
2299        if value in self.values:
2300                return value
2301        self.error(obj, value)
2302
2303    def _choices_str(self, as_rst=False):
2304        """ Returns a description of the trait choices (not none)."""
2305        choices = self.values
2306        if as_rst:
2307            choices = '|'.join('``%r``' % x for x in choices)
2308        else:
2309            choices = repr(list(choices))
2310        return choices
2311
2312    def _info(self, as_rst=False):
2313        """ Returns a description of the trait."""
2314        none = (' or %s' % ('`None`' if as_rst else 'None')
2315                if self.allow_none else
2316                '')
2317        return 'any of %s%s' % (self._choices_str(as_rst), none)
2318
2319    def info(self):
2320        return self._info(as_rst=False)
2321
2322    def info_rst(self):
2323        return self._info(as_rst=True)
2324
2325    def from_string(self, s):
2326        try:
2327            return self.validate(None, s)
2328        except TraitError:
2329            return _safe_literal_eval(s)
2330
2331
2332class CaselessStrEnum(Enum):
2333    """An enum of strings where the case should be ignored."""
2334
2335    def __init__(self, values, default_value=Undefined, **kwargs):
2336        super().__init__(values, default_value=default_value, **kwargs)
2337
2338    def validate(self, obj, value):
2339        if not isinstance(value, str):
2340            self.error(obj, value)
2341
2342        for v in self.values:
2343            if v.lower() == value.lower():
2344                return v
2345        self.error(obj, value)
2346
2347    def _info(self, as_rst=False):
2348        """ Returns a description of the trait."""
2349        none = (' or %s' % ('`None`' if as_rst else 'None')
2350                if self.allow_none else
2351                '')
2352        return 'any of %s (case-insensitive)%s' % (self._choices_str(as_rst), none)
2353
2354    def info(self):
2355        return self._info(as_rst=False)
2356
2357    def info_rst(self):
2358        return self._info(as_rst=True)
2359
2360
2361class FuzzyEnum(Enum):
2362    """An case-ignoring enum matching choices by unique prefixes/substrings."""
2363
2364    case_sensitive = False
2365    #: If True, choices match anywhere in the string, otherwise match prefixes.
2366    substring_matching = False
2367
2368    def __init__(self, values, default_value=Undefined,
2369                 case_sensitive=False, substring_matching=False, **kwargs):
2370        self.case_sensitive = case_sensitive
2371        self.substring_matching = substring_matching
2372        super().__init__(values, default_value=default_value, **kwargs)
2373
2374    def validate(self, obj, value):
2375        if not isinstance(value, str):
2376            self.error(obj, value)
2377
2378        conv_func = (lambda c: c) if self.case_sensitive else lambda c: c.lower()
2379        substring_matching = self.substring_matching
2380        match_func = ((lambda v, c: v in c)
2381                      if substring_matching
2382                      else (lambda v, c: c.startswith(v)))
2383        value = conv_func(value)
2384        choices = self.values
2385        matches = [match_func(value, conv_func(c)) for c in choices]
2386        if sum(matches) == 1:
2387            for v, m in zip(choices, matches):
2388                if m:
2389                    return v
2390
2391        self.error(obj, value)
2392
2393    def _info(self, as_rst=False):
2394        """ Returns a description of the trait."""
2395        none = (' or %s' % ('`None`' if as_rst else 'None')
2396                if self.allow_none else
2397                '')
2398        case = 'sensitive' if self.case_sensitive else 'insensitive'
2399        substr = 'substring' if self.substring_matching else 'prefix'
2400        return 'any case-%s %s of %s%s' % (case, substr,
2401                                           self._choices_str(as_rst),
2402                                           none)
2403
2404    def info(self):
2405        return self._info(as_rst=False)
2406
2407    def info_rst(self):
2408        return self._info(as_rst=True)
2409
2410
2411class Container(Instance):
2412    """An instance of a container (list, set, etc.)
2413
2414    To be subclassed by overriding klass.
2415    """
2416
2417    klass = None
2418    _cast_types = ()
2419    _valid_defaults = SequenceTypes
2420    _trait = None
2421    _literal_from_string_pairs = ("[]", "()")
2422
2423    def __init__(self, trait=None, default_value=Undefined, **kwargs):
2424        """Create a container trait type from a list, set, or tuple.
2425
2426        The default value is created by doing ``List(default_value)``,
2427        which creates a copy of the ``default_value``.
2428
2429        ``trait`` can be specified, which restricts the type of elements
2430        in the container to that TraitType.
2431
2432        If only one arg is given and it is not a Trait, it is taken as
2433        ``default_value``:
2434
2435        ``c = List([1, 2, 3])``
2436
2437        Parameters
2438        ----------
2439        trait : TraitType [ optional ]
2440            the type for restricting the contents of the Container.  If unspecified,
2441            types are not checked.
2442        default_value : SequenceType [ optional ]
2443            The default value for the Trait.  Must be list/tuple/set, and
2444            will be cast to the container type.
2445        allow_none : bool [ default False ]
2446            Whether to allow the value to be None
2447        **kwargs : any
2448            further keys for extensions to the Trait (e.g. config)
2449
2450        """
2451
2452        # allow List([values]):
2453        if trait is not None and default_value is Undefined and not is_trait(trait):
2454            default_value = trait
2455            trait = None
2456
2457        if default_value is None and not kwargs.get("allow_none", False):
2458            # improve backward-compatibility for possible subclasses
2459            # specifying default_value=None as default,
2460            # keeping 'unspecified' behavior (i.e. empty container)
2461            warn(
2462                f"Specifying {self.__class__.__name__}(default_value=None)"
2463                " for no default is deprecated in traitlets 5.0.5."
2464                " Use default_value=Undefined",
2465                DeprecationWarning,
2466                stacklevel=2,
2467            )
2468            default_value = Undefined
2469
2470        if default_value is Undefined:
2471            args = ()
2472        elif default_value is None:
2473            # default_value back on kwargs for super() to handle
2474            args = ()
2475            kwargs["default_value"] = None
2476        elif isinstance(default_value, self._valid_defaults):
2477            args = (default_value,)
2478        else:
2479            raise TypeError(
2480                "default value of %s was %s" % (self.__class__.__name__, default_value)
2481            )
2482
2483        if is_trait(trait):
2484            if isinstance(trait, type):
2485                warn(
2486                    "Traits should be given as instances, not types (for example, `Int()`, not `Int`)."
2487                    " Passing types is deprecated in traitlets 4.1.",
2488                    DeprecationWarning,
2489                    stacklevel=3,
2490                )
2491            self._trait = trait() if isinstance(trait, type) else trait
2492        elif trait is not None:
2493            raise TypeError(
2494                "`trait` must be a Trait or None, got %s" % repr_type(trait)
2495            )
2496
2497        super(Container, self).__init__(klass=self.klass, args=args, **kwargs)
2498
2499    def validate(self, obj, value):
2500        if isinstance(value, self._cast_types):
2501            value = self.klass(value)
2502        value = super(Container, self).validate(obj, value)
2503        if value is None:
2504            return value
2505
2506        value = self.validate_elements(obj, value)
2507
2508        return value
2509
2510    def validate_elements(self, obj, value):
2511        validated = []
2512        if self._trait is None or isinstance(self._trait, Any):
2513            return value
2514        for v in value:
2515            try:
2516                v = self._trait._validate(obj, v)
2517            except TraitError as error:
2518                self.error(obj, v, error)
2519            else:
2520                validated.append(v)
2521        return self.klass(validated)
2522
2523    def class_init(self, cls, name):
2524        if isinstance(self._trait, TraitType):
2525            self._trait.class_init(cls, None)
2526        super(Container, self).class_init(cls, name)
2527
2528    def instance_init(self, obj):
2529        if isinstance(self._trait, TraitType):
2530            self._trait.instance_init(obj)
2531        super(Container, self).instance_init(obj)
2532
2533    def from_string(self, s):
2534        """Load value from a single string"""
2535        if not isinstance(s, str):
2536            raise TraitError(f"Expected string, got {s!r}")
2537        try:
2538            test = literal_eval(s)
2539        except Exception:
2540            test = None
2541        return self.validate(None, test)
2542
2543    def from_string_list(self, s_list):
2544        """Return the value from a list of config strings
2545
2546        This is where we parse CLI configuration
2547        """
2548        if len(s_list) == 1:
2549            # check for deprecated --Class.trait="['a', 'b', 'c']"
2550            r = s_list[0]
2551            if r == "None" and self.allow_none:
2552                return None
2553            if len(r) >= 2 and any(
2554                r.startswith(start) and r.endswith(end)
2555                for start, end in self._literal_from_string_pairs
2556            ):
2557                if self.this_class:
2558                    clsname = self.this_class.__name__ + "."
2559                else:
2560                    clsname = ""
2561
2562                warn(
2563                    "--{0}={1} for containers is deprecated in traitlets 5.0. "
2564                    "You can pass `--{0} item` ... multiple times to add items to a list.".format(
2565                        clsname + self.name, r
2566                    ),
2567                    FutureWarning,
2568                )
2569                return self.klass(literal_eval(r))
2570        sig = inspect.signature(self.item_from_string)
2571        if "index" in sig.parameters:
2572            item_from_string = self.item_from_string
2573        else:
2574            # backward-compat: allow item_from_string to ignore index arg
2575            item_from_string = lambda s, index=None: self.item_from_string(s)
2576        return self.klass(
2577            [item_from_string(s, index=idx) for idx, s in enumerate(s_list)]
2578        )
2579
2580    def item_from_string(self, s, index=None):
2581        """Cast a single item from a string
2582
2583        Evaluated when parsing CLI configuration from a string
2584        """
2585        if self._trait:
2586            return self._trait.from_string(s)
2587        else:
2588            return s
2589
2590
2591class List(Container):
2592    """An instance of a Python list."""
2593    klass = list
2594    _cast_types = (tuple,)
2595
2596    def __init__(
2597        self,
2598        trait=None,
2599        default_value=Undefined,
2600        minlen=0,
2601        maxlen=sys.maxsize,
2602        **kwargs,
2603    ):
2604        """Create a List trait type from a list, set, or tuple.
2605
2606        The default value is created by doing ``list(default_value)``,
2607        which creates a copy of the ``default_value``.
2608
2609        ``trait`` can be specified, which restricts the type of elements
2610        in the container to that TraitType.
2611
2612        If only one arg is given and it is not a Trait, it is taken as
2613        ``default_value``:
2614
2615        ``c = List([1, 2, 3])``
2616
2617        Parameters
2618        ----------
2619        trait : TraitType [ optional ]
2620            the type for restricting the contents of the Container.
2621            If unspecified, types are not checked.
2622        default_value : SequenceType [ optional ]
2623            The default value for the Trait.  Must be list/tuple/set, and
2624            will be cast to the container type.
2625        minlen : Int [ default 0 ]
2626            The minimum length of the input list
2627        maxlen : Int [ default sys.maxsize ]
2628            The maximum length of the input list
2629        """
2630        self._minlen = minlen
2631        self._maxlen = maxlen
2632        super(List, self).__init__(trait=trait, default_value=default_value,
2633                                   **kwargs)
2634
2635    def length_error(self, obj, value):
2636        e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
2637            % (self.name, class_of(obj), self._minlen, self._maxlen, value)
2638        raise TraitError(e)
2639
2640    def validate_elements(self, obj, value):
2641        length = len(value)
2642        if length < self._minlen or length > self._maxlen:
2643            self.length_error(obj, value)
2644
2645        return super(List, self).validate_elements(obj, value)
2646
2647    def set(self, obj, value):
2648        if isinstance(value, str):
2649            return super().set(obj, [value])
2650        else:
2651            return super().set(obj, value)
2652
2653
2654class Set(List):
2655    """An instance of a Python set."""
2656    klass = set
2657    _cast_types = (tuple, list)
2658
2659    _literal_from_string_pairs = ("[]", "()", "{}")
2660
2661    # Redefine __init__ just to make the docstring more accurate.
2662    def __init__(
2663        self,
2664        trait=None,
2665        default_value=Undefined,
2666        minlen=0,
2667        maxlen=sys.maxsize,
2668        **kwargs,
2669    ):
2670        """Create a Set trait type from a list, set, or tuple.
2671
2672        The default value is created by doing ``set(default_value)``,
2673        which creates a copy of the ``default_value``.
2674
2675        ``trait`` can be specified, which restricts the type of elements
2676        in the container to that TraitType.
2677
2678        If only one arg is given and it is not a Trait, it is taken as
2679        ``default_value``:
2680
2681        ``c = Set({1, 2, 3})``
2682
2683        Parameters
2684        ----------
2685        trait : TraitType [ optional ]
2686            the type for restricting the contents of the Container.
2687            If unspecified, types are not checked.
2688        default_value : SequenceType [ optional ]
2689            The default value for the Trait.  Must be list/tuple/set, and
2690            will be cast to the container type.
2691        minlen : Int [ default 0 ]
2692            The minimum length of the input list
2693        maxlen : Int [ default sys.maxsize ]
2694            The maximum length of the input list
2695        """
2696        super(Set, self).__init__(trait, default_value, minlen, maxlen, **kwargs)
2697
2698    def default_value_repr(self):
2699        # Ensure default value is sorted for a reproducible build
2700        list_repr = repr(sorted(self.make_dynamic_default()))
2701        if list_repr == '[]':
2702            return 'set()'
2703        return '{'+list_repr[1:-1]+'}'
2704
2705
2706class Tuple(Container):
2707    """An instance of a Python tuple."""
2708
2709    klass = tuple
2710    _cast_types = (list,)
2711
2712    def __init__(self, *traits, **kwargs):
2713        """Create a tuple from a list, set, or tuple.
2714
2715        Create a fixed-type tuple with Traits:
2716
2717        ``t = Tuple(Int(), Str(), CStr())``
2718
2719        would be length 3, with Int,Str,CStr for each element.
2720
2721        If only one arg is given and it is not a Trait, it is taken as
2722        default_value:
2723
2724        ``t = Tuple((1, 2, 3))``
2725
2726        Otherwise, ``default_value`` *must* be specified by keyword.
2727
2728        Parameters
2729        ----------
2730        *traits : TraitTypes [ optional ]
2731            the types for restricting the contents of the Tuple.  If unspecified,
2732            types are not checked. If specified, then each positional argument
2733            corresponds to an element of the tuple.  Tuples defined with traits
2734            are of fixed length.
2735        default_value : SequenceType [ optional ]
2736            The default value for the Tuple.  Must be list/tuple/set, and
2737            will be cast to a tuple. If ``traits`` are specified,
2738            ``default_value`` must conform to the shape and type they specify.
2739        **kwargs
2740            Other kwargs passed to `Container`
2741        """
2742        default_value = kwargs.pop("default_value", Undefined)
2743        # allow Tuple((values,)):
2744        if len(traits) == 1 and default_value is Undefined and not is_trait(traits[0]):
2745            default_value = traits[0]
2746            traits = ()
2747
2748        if default_value is None and not kwargs.get("allow_none", False):
2749            # improve backward-compatibility for possible subclasses
2750            # specifying default_value=None as default,
2751            # keeping 'unspecified' behavior (i.e. empty container)
2752            warn(
2753                f"Specifying {self.__class__.__name__}(default_value=None)"
2754                " for no default is deprecated in traitlets 5.0.5."
2755                " Use default_value=Undefined",
2756                DeprecationWarning,
2757                stacklevel=2,
2758            )
2759            default_value = Undefined
2760
2761        if default_value is Undefined:
2762            args = ()
2763        elif default_value is None:
2764            # default_value back on kwargs for super() to handle
2765            args = ()
2766            kwargs["default_value"] = None
2767        elif isinstance(default_value, self._valid_defaults):
2768            args = (default_value,)
2769        else:
2770            raise TypeError(
2771                "default value of %s was %s" % (self.__class__.__name__, default_value)
2772            )
2773
2774        self._traits = []
2775        for trait in traits:
2776            if isinstance(trait, type):
2777                warn(
2778                    "Traits should be given as instances, not types (for example, `Int()`, not `Int`)"
2779                    " Passing types is deprecated in traitlets 4.1.",
2780                    DeprecationWarning,
2781                    stacklevel=2,
2782                )
2783                trait = trait()
2784            self._traits.append(trait)
2785
2786        if self._traits and (default_value is None or default_value is Undefined):
2787            # don't allow default to be an empty container if length is specified
2788            args = None
2789        super(Container, self).__init__(klass=self.klass, args=args, **kwargs)
2790
2791    def item_from_string(self, s, index):
2792        """Cast a single item from a string
2793
2794        Evaluated when parsing CLI configuration from a string
2795        """
2796        if not self._traits or index >= len(self._traits):
2797            # return s instead of raising index error
2798            # length errors will be raised later on validation
2799            return s
2800        return self._traits[index].from_string(s)
2801
2802    def validate_elements(self, obj, value):
2803        if not self._traits:
2804            # nothing to validate
2805            return value
2806        if len(value) != len(self._traits):
2807            e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
2808                % (self.name, class_of(obj), len(self._traits), repr_type(value))
2809            raise TraitError(e)
2810
2811        validated = []
2812        for t, v in zip(self._traits, value):
2813            try:
2814                v = t._validate(obj, v)
2815            except TraitError as error:
2816                self.error(obj, v, error)
2817            else:
2818                validated.append(v)
2819        return tuple(validated)
2820
2821    def class_init(self, cls, name):
2822        for trait in self._traits:
2823            if isinstance(trait, TraitType):
2824                trait.class_init(cls, None)
2825        super(Container, self).class_init(cls, name)
2826
2827    def instance_init(self, obj):
2828        for trait in self._traits:
2829            if isinstance(trait, TraitType):
2830                trait.instance_init(obj)
2831        super(Container, self).instance_init(obj)
2832
2833
2834class Dict(Instance):
2835    """An instance of a Python dict.
2836
2837    One or more traits can be passed to the constructor
2838    to validate the keys and/or values of the dict.
2839    If you need more detailed validation,
2840    you may use a custom validator method.
2841
2842    .. versionchanged:: 5.0
2843        Added key_trait for validating dict keys.
2844
2845    .. versionchanged:: 5.0
2846        Deprecated ambiguous ``trait``, ``traits`` args in favor of ``value_trait``, ``per_key_traits``.
2847    """
2848    _value_trait = None
2849    _key_trait = None
2850
2851    def __init__(self, value_trait=None, per_key_traits=None, key_trait=None, default_value=Undefined,
2852                 **kwargs):
2853        """Create a dict trait type from a Python dict.
2854
2855        The default value is created by doing ``dict(default_value)``,
2856        which creates a copy of the ``default_value``.
2857
2858        Parameters
2859        ----------
2860        value_trait : TraitType [ optional ]
2861            The specified trait type to check and use to restrict the values of
2862            the dict. If unspecified, values are not checked.
2863        per_key_traits : Dictionary of {keys:trait types} [ optional, keyword-only ]
2864            A Python dictionary containing the types that are valid for
2865            restricting the values of the dict on a per-key basis.
2866            Each value in this dict should be a Trait for validating
2867        key_trait : TraitType [ optional, keyword-only ]
2868            The type for restricting the keys of the dict. If
2869            unspecified, the types of the keys are not checked.
2870        default_value : SequenceType [ optional, keyword-only ]
2871            The default value for the Dict.  Must be dict, tuple, or None, and
2872            will be cast to a dict if not None. If any key or value traits are specified,
2873            the `default_value` must conform to the constraints.
2874
2875        Examples
2876        --------
2877        >>> d = Dict(Unicode())
2878        a dict whose values must be text
2879
2880        >>> d2 = Dict(per_key_traits={"n": Integer(), "s": Unicode()})
2881        d2['n'] must be an integer
2882        d2['s'] must be text
2883
2884        >>> d3 = Dict(value_trait=Integer(), key_trait=Unicode())
2885        d3's keys must be text
2886        d3's values must be integers
2887        """
2888
2889        # handle deprecated keywords
2890        trait = kwargs.pop('trait', None)
2891        if trait is not None:
2892            if value_trait is not None:
2893                raise TypeError("Found a value for both `value_trait` and its deprecated alias `trait`.")
2894            value_trait = trait
2895            warn(
2896                "Keyword `trait` is deprecated in traitlets 5.0, use `value_trait` instead",
2897                DeprecationWarning,
2898                stacklevel=2,
2899            )
2900        traits = kwargs.pop("traits", None)
2901        if traits is not None:
2902            if per_key_traits is not None:
2903                raise TypeError("Found a value for both `per_key_traits` and its deprecated alias `traits`.")
2904            per_key_traits = traits
2905            warn(
2906                "Keyword `traits` is deprecated in traitlets 5.0, use `per_key_traits` instead",
2907                DeprecationWarning,
2908                stacklevel=2,
2909            )
2910
2911        # Handling positional arguments
2912        if default_value is Undefined and value_trait is not None:
2913            if not is_trait(value_trait):
2914                default_value = value_trait
2915                value_trait = None
2916
2917        if key_trait is None and per_key_traits is not None:
2918            if is_trait(per_key_traits):
2919                key_trait = per_key_traits
2920                per_key_traits = None
2921
2922        # Handling default value
2923        if default_value is Undefined:
2924            default_value = {}
2925        if default_value is None:
2926            args = None
2927        elif isinstance(default_value, dict):
2928            args = (default_value,)
2929        elif isinstance(default_value, SequenceTypes):
2930            args = (default_value,)
2931        else:
2932            raise TypeError('default value of Dict was %s' % default_value)
2933
2934        # Case where a type of TraitType is provided rather than an instance
2935        if is_trait(value_trait):
2936            if isinstance(value_trait, type):
2937                warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)"
2938                     " Passing types is deprecated in traitlets 4.1.",
2939                     DeprecationWarning, stacklevel=2)
2940                value_trait = value_trait()
2941            self._value_trait = value_trait
2942        elif value_trait is not None:
2943            raise TypeError("`value_trait` must be a Trait or None, got %s" % repr_type(value_trait))
2944
2945        if is_trait(key_trait):
2946            if isinstance(key_trait, type):
2947                warn("Traits should be given as instances, not types (for example, `Int()`, not `Int`)"
2948                     " Passing types is deprecated in traitlets 4.1.",
2949                     DeprecationWarning, stacklevel=2)
2950                key_trait = key_trait()
2951            self._key_trait = key_trait
2952        elif key_trait is not None:
2953            raise TypeError("`key_trait` must be a Trait or None, got %s" % repr_type(key_trait))
2954
2955        self._per_key_traits = per_key_traits
2956
2957        super(Dict, self).__init__(klass=dict, args=args, **kwargs)
2958
2959    def element_error(self, obj, element, validator, side='Values'):
2960        e = side + " of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
2961            % (self.name, class_of(obj), validator.info(), repr_type(element))
2962        raise TraitError(e)
2963
2964    def validate(self, obj, value):
2965        value = super(Dict, self).validate(obj, value)
2966        if value is None:
2967            return value
2968        value = self.validate_elements(obj, value)
2969        return value
2970
2971    def validate_elements(self, obj, value):
2972        per_key_override = self._per_key_traits or {}
2973        key_trait = self._key_trait
2974        value_trait = self._value_trait
2975        if not (key_trait or value_trait or per_key_override):
2976            return value
2977
2978        validated = {}
2979        for key in value:
2980            v = value[key]
2981            if key_trait:
2982                try:
2983                    key = key_trait._validate(obj, key)
2984                except TraitError as error:
2985                    self.element_error(obj, key, key_trait, 'Keys')
2986            active_value_trait = per_key_override.get(key, value_trait)
2987            if active_value_trait:
2988                try:
2989                    v = active_value_trait._validate(obj, v)
2990                except TraitError:
2991                    self.element_error(obj, v, active_value_trait, 'Values')
2992            validated[key] = v
2993
2994        return self.klass(validated)
2995
2996    def class_init(self, cls, name):
2997        if isinstance(self._value_trait, TraitType):
2998            self._value_trait.class_init(cls, None)
2999        if isinstance(self._key_trait, TraitType):
3000            self._key_trait.class_init(cls, None)
3001        if self._per_key_traits is not None:
3002            for trait in self._per_key_traits.values():
3003                trait.class_init(cls, None)
3004        super(Dict, self).class_init(cls, name)
3005
3006    def instance_init(self, obj):
3007        if isinstance(self._value_trait, TraitType):
3008            self._value_trait.instance_init(obj)
3009        if isinstance(self._key_trait, TraitType):
3010            self._key_trait.instance_init(obj)
3011        if self._per_key_traits is not None:
3012            for trait in self._per_key_traits.values():
3013                trait.instance_init(obj)
3014        super(Dict, self).instance_init(obj)
3015
3016    def from_string(self, s):
3017        """Load value from a single string"""
3018        if not isinstance(s, str):
3019            raise TypeError(f"from_string expects a string, got {repr(s)} of type {type(s)}")
3020        try:
3021            return self.from_string_list([s])
3022        except Exception:
3023            test = _safe_literal_eval(s)
3024            if isinstance(test, dict):
3025                return test
3026            raise
3027
3028    def from_string_list(self, s_list):
3029        """Return a dict from a list of config strings.
3030
3031        This is where we parse CLI configuration.
3032
3033        Each item should have the form ``"key=value"``.
3034
3035        item parsing is done in :meth:`.item_from_string`.
3036        """
3037        if len(s_list) == 1 and s_list[0] == "None" and self.allow_none:
3038            return None
3039        if (
3040            len(s_list) == 1
3041            and s_list[0].startswith("{")
3042            and s_list[0].endswith("}")
3043        ):
3044            warn(
3045                "--{0}={1} for dict-traits is deprecated in traitlets 5.0. "
3046                "You can pass --{0} <key=value> ... multiple times to add items to a dict.".format(
3047                    self.name,
3048                    s_list[0],
3049                ),
3050                FutureWarning,
3051            )
3052
3053            return literal_eval(s_list[0])
3054
3055        combined = {}
3056        for d in [self.item_from_string(s) for s in s_list]:
3057            combined.update(d)
3058        return combined
3059
3060    def item_from_string(self, s):
3061        """Cast a single-key dict from a string.
3062
3063        Evaluated when parsing CLI configuration from a string.
3064
3065        Dicts expect strings of the form key=value.
3066
3067        Returns a one-key dictionary,
3068        which will be merged in :meth:`.from_string_list`.
3069        """
3070
3071        if '=' not in s:
3072            raise TraitError(
3073                "'%s' options must have the form 'key=value', got %s"
3074                % (self.__class__.__name__, repr(s),)
3075            )
3076        key, value = s.split("=", 1)
3077
3078        # cast key with key trait, if defined
3079        if self._key_trait:
3080            key = self._key_trait.from_string(key)
3081
3082        # cast value with value trait, if defined (per-key or global)
3083        value_trait = (self._per_key_traits or {}).get(key, self._value_trait)
3084        if value_trait:
3085            value = value_trait.from_string(value)
3086        return {key: value}
3087
3088
3089class TCPAddress(TraitType):
3090    """A trait for an (ip, port) tuple.
3091
3092    This allows for both IPv4 IP addresses as well as hostnames.
3093    """
3094
3095    default_value = ('127.0.0.1', 0)
3096    info_text = 'an (ip, port) tuple'
3097
3098    def validate(self, obj, value):
3099        if isinstance(value, tuple):
3100            if len(value) == 2:
3101                if isinstance(value[0], str) and isinstance(value[1], int):
3102                    port = value[1]
3103                    if port >= 0 and port <= 65535:
3104                        return value
3105        self.error(obj, value)
3106
3107    def from_string(self, s):
3108        if self.allow_none and s == 'None':
3109            return None
3110        if ':' not in s:
3111            raise ValueError('Require `ip:port`, got %r' % s)
3112        ip, port = s.split(':', 1)
3113        port = int(port)
3114        return (ip, port)
3115
3116
3117class CRegExp(TraitType):
3118    """A casting compiled regular expression trait.
3119
3120    Accepts both strings and compiled regular expressions. The resulting
3121    attribute will be a compiled regular expression."""
3122
3123    info_text = 'a regular expression'
3124
3125    def validate(self, obj, value):
3126        try:
3127            return re.compile(value)
3128        except Exception:
3129            self.error(obj, value)
3130
3131
3132class UseEnum(TraitType):
3133    """Use a Enum class as model for the data type description.
3134    Note that if no default-value is provided, the first enum-value is used
3135    as default-value.
3136
3137    .. sourcecode:: python
3138
3139        # -- SINCE: Python 3.4 (or install backport: pip install enum34)
3140        import enum
3141        from traitlets import HasTraits, UseEnum
3142
3143        class Color(enum.Enum):
3144            red = 1         # -- IMPLICIT: default_value
3145            blue = 2
3146            green = 3
3147
3148        class MyEntity(HasTraits):
3149            color = UseEnum(Color, default_value=Color.blue)
3150
3151        entity = MyEntity(color=Color.red)
3152        entity.color = Color.green    # USE: Enum-value (preferred)
3153        entity.color = "green"        # USE: name (as string)
3154        entity.color = "Color.green"  # USE: scoped-name (as string)
3155        entity.color = 3              # USE: number (as int)
3156        assert entity.color is Color.green
3157    """
3158    default_value = None
3159    info_text = "Trait type adapter to a Enum class"
3160
3161    def __init__(self, enum_class, default_value=None, **kwargs):
3162        assert issubclass(enum_class, enum.Enum), \
3163                          "REQUIRE: enum.Enum, but was: %r" % enum_class
3164        allow_none = kwargs.get("allow_none", False)
3165        if default_value is None and not allow_none:
3166            default_value = list(enum_class.__members__.values())[0]
3167        super(UseEnum, self).__init__(default_value=default_value, **kwargs)
3168        self.enum_class = enum_class
3169        self.name_prefix = enum_class.__name__ + "."
3170
3171    def select_by_number(self, value, default=Undefined):
3172        """Selects enum-value by using its number-constant."""
3173        assert isinstance(value, int)
3174        enum_members = self.enum_class.__members__
3175        for enum_item in enum_members.values():
3176            if enum_item.value == value:
3177                return enum_item
3178        # -- NOT FOUND:
3179        return default
3180
3181    def select_by_name(self, value, default=Undefined):
3182        """Selects enum-value by using its name or scoped-name."""
3183        assert isinstance(value, str)
3184        if value.startswith(self.name_prefix):
3185            # -- SUPPORT SCOPED-NAMES, like: "Color.red" => "red"
3186            value = value.replace(self.name_prefix, "", 1)
3187        return self.enum_class.__members__.get(value, default)
3188
3189    def validate(self, obj, value):
3190        if isinstance(value, self.enum_class):
3191            return value
3192        elif isinstance(value, int):
3193            # -- CONVERT: number => enum_value (item)
3194            value2 = self.select_by_number(value)
3195            if value2 is not Undefined:
3196                return value2
3197        elif isinstance(value, str):
3198            # -- CONVERT: name or scoped_name (as string) => enum_value (item)
3199            value2 = self.select_by_name(value)
3200            if value2 is not Undefined:
3201                return value2
3202        elif value is None:
3203            if self.allow_none:
3204                return None
3205            else:
3206                return self.default_value
3207        self.error(obj, value)
3208
3209    def _choices_str(self, as_rst=False):
3210        """ Returns a description of the trait choices (not none)."""
3211        choices = self.enum_class.__members__.keys()
3212        if as_rst:
3213            return '|'.join('``%r``' % x for x in choices)
3214        else:
3215            return repr(list(choices))  # Listify because py3.4- prints odict-class
3216
3217    def _info(self, as_rst=False):
3218        """ Returns a description of the trait."""
3219        none = (' or %s' % ('`None`' if as_rst else 'None')
3220                if self.allow_none else
3221                '')
3222        return 'any of %s%s' % (self._choices_str(as_rst), none)
3223
3224    def info(self):
3225        return self._info(as_rst=False)
3226
3227    def info_rst(self):
3228        return self._info(as_rst=True)
3229
3230
3231
3232class Callable(TraitType):
3233    """A trait which is callable.
3234
3235    Notes
3236    -----
3237    Classes are callable, as are instances
3238    with a __call__() method."""
3239
3240    info_text = 'a callable'
3241
3242    def validate(self, obj, value):
3243        if callable(value):
3244            return value
3245        else:
3246            self.error(obj, value)
3247
3248
3249def _add_all():
3250    """add all trait types to `__all__`
3251
3252    do in a function to avoid iterating through globals while defining local variables
3253    """
3254    for _name, _value in globals().items():
3255        if not _name.startswith('_') and isinstance(_value, type) and issubclass(_value, TraitType):
3256            __all__.append(_name)
3257
3258_add_all()
3259