Source code for traits.ctrait

# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" Defines the core CTrait class.

The CTrait class extends the C-level cTrait type to provide the full CTrait
API. CTraits are the core objects that are used to generate defaults and
validate as well as maintaining a list of notifiers and calling them when
values are modified.
"""

import inspect

from . import ctraits
from .constants import ComparisonMode, DefaultValue, default_value_map
from .observation.i_observable import IObservable
from .trait_base import SequenceTypes, Undefined
from .trait_dict_object import TraitDictObject
from .trait_list_object import TraitListObject
from .trait_set_object import TraitSetObject


def __newobj__(cls, *args):
    """ Unpickles new-style objects.
    """
    return cls.__new__(cls, *args)


[docs]@IObservable.register class CTrait(ctraits.cTrait): """ Extends the underlying C-based cTrait type. """ def __call__(self, *args, **metadata): """ Allows a derivative trait to be defined from this one. """ from .trait_type import TraitType from .traits import Trait handler = self.handler if isinstance(handler, TraitType): dict = (self.__dict__ or {}).copy() dict.update(metadata) return handler(*args, **dict) metadata.setdefault("parent", self) return Trait(*(args + (self,)), **metadata) @property def default(self): kind, value = self.default_value() if kind in ( DefaultValue.object, DefaultValue.callable_and_args, DefaultValue.callable, DefaultValue.disallow, ): return Undefined elif kind in ( DefaultValue.dict_copy, DefaultValue.trait_dict_object, DefaultValue.trait_set_object, DefaultValue.list_copy, DefaultValue.trait_list_object, ): return value.copy() elif kind in {DefaultValue.constant, DefaultValue.missing}: return value else: # This shouldn't ever happen. raise RuntimeError( "Unexpected default value kind: {!r}".format(kind) ) @property def default_kind(self): return default_value_map[self.default_value()[0]] @property def trait_type(self): handler = self.handler if handler is not None: return handler else: from .trait_types import Any return Any @property def inner_traits(self): handler = self.handler if handler is not None: return handler.inner_traits() return () @property def comparison_mode(self): """ Get or set the comparison mode on the trait. Getter returns a ComparisonMode enum. Setter acceps either an int or a ComparisonMode enum. """ i_comparison_mode = super().comparison_mode return ComparisonMode(i_comparison_mode) @comparison_mode.setter def comparison_mode(self, value): ctraits.cTrait.comparison_mode.__set__(self, value) @property def property_fields(self): """ Return a tuple of callables (fget, fset, validate) for the property trait.""" return self._get_property() @property_fields.setter def property_fields(self, value): """ Set the fget, fset, validate callables for the property. Parameters ---------- value : tuple Value should be the tuple of callables (fget, fset, validate). """ func_arg_counts = [] for arg in value: if arg is None: nargs = 0 else: sig = inspect.signature(arg) nargs = len(sig.parameters) func_arg_counts.extend([arg, nargs]) self._set_property(*func_arg_counts)
[docs] def is_trait_type(self, trait_type): """ Returns whether or not this trait is of a specified trait type. """ return isinstance(self.trait_type, trait_type)
[docs] def get_editor(self): """ Returns the user interface editor associated with the trait. """ from traitsui.api import EditorFactory # See if we have an editor: editor = self.editor if editor is None: # Else see if the trait handler has an editor: handler = self.handler if handler is not None: editor = handler.get_editor(self) # If not, give up and use a default text editor: if editor is None: from traitsui.api import TextEditor editor = TextEditor # If the result is not an EditorFactory: if not isinstance(editor, EditorFactory): # Then it should be a factory for creating them: args = () traits = {} if type(editor) in SequenceTypes: for item in editor[:]: if type(item) in SequenceTypes: args = tuple(item) elif isinstance(item, dict): traits = item if traits.get("trait", 0) is None: traits = traits.copy() traits["trait"] = self else: editor = item editor = editor(*args, **traits) # Cache the result: self.editor = editor # Return the resulting EditorFactory object: return editor
[docs] def get_help(self, full=True): """ Returns the help text for a trait. If *full* is False or the trait does not have a **help** string, the returned string is constructed from the **desc** attribute on the trait and the **info** string on the trait's handler. Parameters ---------- full : bool Indicates whether to return the value of the *help* attribute of the trait itself. """ if full: help = self.help if help is not None: return help handler = self.handler if handler is not None: info = "must be %s." % handler.info() else: info = "may be any value." desc = self.desc if self.desc is None: return info.capitalize() return "Specifies %s and %s" % (desc, info)
[docs] def full_info(self, object, name, value): """ Returns a description of the trait. """ handler = self.handler if handler is not None: return handler.full_info(object, name, value) return "any value"
[docs] def info(self): """ Returns a description of the trait. """ handler = self.handler if handler is not None: return handler.info() return "any value"
[docs] def as_ctrait(self): """ Method that returns self for trait converters. """ return self
def __reduce_ex__(self, protocol): """ Returns the pickleable form of a CTrait object. """ return (__newobj__, (self.__class__, 0), self.__getstate__())
def _adapt_wrapper(*args, **kw): # We need this wrapper to defer the import of 'adapt' and avoid a circular # import. The ctraits 'adapt' callback needs to be set as soon as possible, # but the adaptation mechanism relies on traits. # This wrapper is called once, after which we set the ctraits callback # to point directly to 'adapt'. from traits.adaptation.api import adapt ctraits._adapt(adapt) return adapt(*args, **kw) # Make sure the Python-level version of the trait class is known to all # interested parties: ctraits._ctrait(CTrait) #: Register Trait container object classes with ctraits.c ctraits._list_classes(TraitListObject, TraitSetObject, TraitDictObject) #: Tell the C-based traits module about the traits adaptation 'adapt' function ctraits._adapt(_adapt_wrapper)