File: naming_traits.py

package info (click to toggle)
python-apptools 4.4.0-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 2,652 kB
  • sloc: python: 16,657; makefile: 77
file content (177 lines) | stat: -rw-r--r-- 6,243 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
#-------------------------------------------------------------------------------
#
#  Defines the NamingLog and NamingIndex traits
#
#  Written by: David C. Morrill
#
#  Date: 08/15/2005
#
#  (c) Copyright 2005 by Enthought, Inc.
#
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#  Imports:
#-------------------------------------------------------------------------------

import sys

from traits.api \
    import Trait, TraitHandler, TraitFactory

from traits.trait_base \
    import class_of, get_module_name

try:
    from traitsui.api \
        import DropEditor
except ImportError:
    pass

from apptools.naming.api \
    import Binding


#-------------------------------------------------------------------------------
#  'NamingInstance' trait factory:
#-------------------------------------------------------------------------------

def NamingInstance ( klass = None, value = '', allow_none = False, **metadata ):
    metadata.setdefault( 'copy', 'deep' )
    return Trait( value, NamingTraitHandler( klass, or_none = allow_none,
                  module = get_module_name() ), **metadata )

NamingInstance = TraitFactory( NamingInstance )

#-------------------------------------------------------------------------------
#  'NamingTraitHandler' class:
#-------------------------------------------------------------------------------

class NamingTraitHandler ( TraitHandler ):

    #---------------------------------------------------------------------------
    #  Initializes the object:
    #---------------------------------------------------------------------------

    def __init__ ( self, aClass, or_none, module ):
        """ Initializes the object.
        """
        self.or_none = (or_none != False)
        self.module  = module
        self.aClass  = aClass
        if (aClass is not None) \
            and (not isinstance( aClass, ( basestring, type ) )):
            self.aClass = aClass.__class__

    def validate ( self, object, name, value ):
        if isinstance( value, basestring ):
            if value == '':
                if self.or_none:
                    return ''
                else:
                    self.validate_failed( object, name, value )
            try:
                value = self._get_binding_for( value )
            except:
                self.validate_failed( object, name, value )

        if isinstance(self.aClass, basestring):
            self.resolve_class( object, name, value )

        if (isinstance( value, Binding ) and
            ((self.aClass is None) or isinstance( value.obj, self.aClass ))):
            return value.namespace_name
        self.validate_failed( object, name, value )

    def info ( self ):
        aClass = self.aClass
        if aClass is None:
            result = 'path'
        else:
            if type( aClass ) is not str:
                aClass = aClass.__name__
            result = 'path to an instance of ' + class_of( aClass )
        if self.or_none is None:
            return result + ' or an empty string'
        return result

    def validate_failed ( self, object, name, value ):
        if not isinstance( value, type ):
            msg = 'class %s' % value.__class__.__name__
        else:
            msg = '%s (i.e. %s)' % ( str( type( value ) )[1:-1], repr( value ) )
        self.error( object, name, msg )

    def get_editor ( self, trait ):
        if self.editor is None:
            try:
                from traitsui.api import DropEditor

                self.editor = DropEditor( klass    = self.aClass,
                                          binding  = True,
                                          readonly = False )
            except ImportError:
                pass

        return self.editor

    def post_setattr ( self, object, name, value ):
        other = None
        if value != '':
            other = self._get_binding_for( value ).obj
        object.__dict__[ name + '_' ] = other

    def _get_binding_for ( self, value ):

        result = None

        # FIXME: The following code makes this whole component have a dependency
        # on envisage, and worse, assumes the use of a particular project
        # plugin!  This is horrible and should be refactored out, possibly to
        # a custom sub-class of whoever needs this behavior.
        try:
            from envisage import get_application
            workspace = get_application().service_registry.get_service(
                            'envisage.project.IWorkspace' )
            result = workspace.lookup_binding( value )
        except ImportError:
            pass

        return result


    def resolve_class ( self, object, name, value ):
        aClass = self.find_class()
        if aClass is None:
            self.validate_failed( object, name, value )
        self.aClass = aClass

        # fixme: The following is quite ugly, because it wants to try and fix
        # the trait referencing this handler to use the 'fast path' now that the
        # actual class has been resolved. The problem is finding the trait,
        # especially in the case of List(Instance('foo')), where the
        # object.base_trait(...) value is the List trait, not the Instance
        # trait, so we need to check for this and pull out the List
        # 'item_trait'. Obviously this does not extend well to other traits
        # containing nested trait references (Dict?)...
        trait   = object.base_trait( name )
        handler = trait.handler
        if (handler is not self) and hasattr( handler, 'item_trait' ):
            trait = handler.item_trait
        trait.validate( self.fast_validate )

    def find_class ( self ):
        module = self.module
        aClass = self.aClass
        col    = aClass.rfind( '.' )
        if col >= 0:
            module = aClass[ : col ]
            aClass = aClass[ col + 1: ]
        theClass = getattr( sys.modules.get( module ), aClass, None )
        if (theClass is None) and (col >= 0):
            try:
                theClass = getattr( __import__( module ), aClass, None )
            except:
                pass
        return theClass