File: ctrait.py

package info (click to toggle)
python-traits 6.4.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 8,648 kB
  • sloc: python: 34,801; ansic: 4,266; makefile: 102
file content (276 lines) | stat: -rw-r--r-- 8,422 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# (C) Copyright 2005-2023 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)


@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)

    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)

    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

    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)

    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"

    def info(self):
        """ Returns a description of the trait.
        """
        handler = self.handler
        if handler is not None:
            return handler.info()

        return "any value"

    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)