Source code for apptools.naming.trait_defs.naming_traits
# (C) Copyright 2005-2024 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!
# -------------------------------------------------------------------------------
# Imports:
# -------------------------------------------------------------------------------
import sys
from traits.api import Trait, TraitHandler, TraitFactory
from traits.trait_base import class_of, get_module_name
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:
# -------------------------------------------------------------------------------
[docs]class NamingTraitHandler(TraitHandler):
# ---------------------------------------------------------------------------
# Initializes the object:
# ---------------------------------------------------------------------------
def __init__(self, aClass, or_none, module):
"""Initializes the object."""
self.or_none = or_none is not False
self.module = module
self.aClass = aClass
if (aClass is not None) and (
not isinstance(aClass, (str, type))
):
self.aClass = aClass.__class__
[docs] def validate(self, object, name, value):
if isinstance(value, str):
if value == "":
if self.or_none:
return ""
else:
self.validate_failed(object, name, value)
try:
value = self._get_binding_for(value)
except: # noqa: E722
self.validate_failed(object, name, value)
if isinstance(self.aClass, str):
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)
[docs] 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
[docs] 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)
[docs] def get_editor(self, trait):
if self.editor is None:
from traitsui.api import DropEditor
self.editor = DropEditor(
klass=self.aClass, binding=True, readonly=False
)
return self.editor
[docs] 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
[docs] 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)
[docs] 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 Exception:
pass
return theClass