1#------------------------------------------------------------------------------- 2# 3# Defines the NamingLog and NamingIndex traits 4# 5# Written by: David C. Morrill 6# 7# Date: 08/15/2005 8# 9# (c) Copyright 2005 by Enthought, Inc. 10# 11#------------------------------------------------------------------------------- 12 13#------------------------------------------------------------------------------- 14# Imports: 15#------------------------------------------------------------------------------- 16 17import sys 18 19import six 20 21from traits.api \ 22 import Trait, TraitHandler, TraitFactory 23 24from traits.trait_base \ 25 import class_of, get_module_name 26 27from traitsui.api \ 28 import DropEditor 29 30from apptools.naming.api \ 31 import Binding 32 33 34#------------------------------------------------------------------------------- 35# 'NamingInstance' trait factory: 36#------------------------------------------------------------------------------- 37 38def NamingInstance ( klass = None, value = '', allow_none = False, **metadata ): 39 metadata.setdefault( 'copy', 'deep' ) 40 return Trait( value, NamingTraitHandler( klass, or_none = allow_none, 41 module = get_module_name() ), **metadata ) 42 43NamingInstance = TraitFactory( NamingInstance ) 44 45#------------------------------------------------------------------------------- 46# 'NamingTraitHandler' class: 47#------------------------------------------------------------------------------- 48 49class NamingTraitHandler ( TraitHandler ): 50 51 #--------------------------------------------------------------------------- 52 # Initializes the object: 53 #--------------------------------------------------------------------------- 54 55 def __init__ ( self, aClass, or_none, module ): 56 """ Initializes the object. 57 """ 58 self.or_none = (or_none != False) 59 self.module = module 60 self.aClass = aClass 61 if (aClass is not None) \ 62 and (not isinstance( aClass, ( six.string_types, type ) )): 63 self.aClass = aClass.__class__ 64 65 def validate ( self, object, name, value ): 66 if isinstance( value, six.string_types ): 67 if value == '': 68 if self.or_none: 69 return '' 70 else: 71 self.validate_failed( object, name, value ) 72 try: 73 value = self._get_binding_for( value ) 74 except: 75 self.validate_failed( object, name, value ) 76 77 if isinstance(self.aClass, six.string_types): 78 self.resolve_class( object, name, value ) 79 80 if (isinstance( value, Binding ) and 81 ((self.aClass is None) or isinstance( value.obj, self.aClass ))): 82 return value.namespace_name 83 self.validate_failed( object, name, value ) 84 85 def info ( self ): 86 aClass = self.aClass 87 if aClass is None: 88 result = 'path' 89 else: 90 if type( aClass ) is not str: 91 aClass = aClass.__name__ 92 result = 'path to an instance of ' + class_of( aClass ) 93 if self.or_none is None: 94 return result + ' or an empty string' 95 return result 96 97 def validate_failed ( self, object, name, value ): 98 if not isinstance( value, type ): 99 msg = 'class %s' % value.__class__.__name__ 100 else: 101 msg = '%s (i.e. %s)' % ( str( type( value ) )[1:-1], repr( value ) ) 102 self.error( object, name, msg ) 103 104 def get_editor ( self, trait ): 105 if self.editor is None: 106 from traitsui.api import DropEditor 107 108 self.editor = DropEditor( klass = self.aClass, 109 binding = True, 110 readonly = False ) 111 return self.editor 112 113 def post_setattr ( self, object, name, value ): 114 other = None 115 if value != '': 116 other = self._get_binding_for( value ).obj 117 object.__dict__[ name + '_' ] = other 118 119 def _get_binding_for ( self, value ): 120 121 result = None 122 123 # FIXME: The following code makes this whole component have a dependency 124 # on envisage, and worse, assumes the use of a particular project 125 # plugin! This is horrible and should be refactored out, possibly to 126 # a custom sub-class of whoever needs this behavior. 127 try: 128 from envisage import get_application 129 workspace = get_application().service_registry.get_service( 130 'envisage.project.IWorkspace' ) 131 result = workspace.lookup_binding( value ) 132 except ImportError: 133 pass 134 135 return result 136 137 138 def resolve_class ( self, object, name, value ): 139 aClass = self.find_class() 140 if aClass is None: 141 self.validate_failed( object, name, value ) 142 self.aClass = aClass 143 144 # fixme: The following is quite ugly, because it wants to try and fix 145 # the trait referencing this handler to use the 'fast path' now that the 146 # actual class has been resolved. The problem is finding the trait, 147 # especially in the case of List(Instance('foo')), where the 148 # object.base_trait(...) value is the List trait, not the Instance 149 # trait, so we need to check for this and pull out the List 150 # 'item_trait'. Obviously this does not extend well to other traits 151 # containing nested trait references (Dict?)... 152 trait = object.base_trait( name ) 153 handler = trait.handler 154 if (handler is not self) and hasattr( handler, 'item_trait' ): 155 trait = handler.item_trait 156 trait.validate( self.fast_validate ) 157 158 def find_class ( self ): 159 module = self.module 160 aClass = self.aClass 161 col = aClass.rfind( '.' ) 162 if col >= 0: 163 module = aClass[ : col ] 164 aClass = aClass[ col + 1: ] 165 theClass = getattr( sys.modules.get( module ), aClass, None ) 166 if (theClass is None) and (col >= 0): 167 try: 168 theClass = getattr( __import__( module ), aClass, None ) 169 except: 170 pass 171 return theClass 172 173