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)