PageRenderTime 25ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/traits-4.2.0/traits/adapter.py

#
Python | 234 lines | 116 code | 30 blank | 88 comment | 3 complexity | 9caf833b5adbfc10d8920ef55b05044c MD5 | raw file
Possible License(s): EPL-1.0, LGPL-2.1, BSD-3-Clause
  1. #-------------------------------------------------------------------------------
  2. #
  3. # Copyright (c) 2007, Enthought, Inc.
  4. # All rights reserved.
  5. #
  6. # This software is provided without warranty under the terms of the BSD
  7. # license included in /LICENSE.txt and may be redistributed only
  8. # under the conditions described in the aforementioned license. The license
  9. # is also available online at http://www.enthought.com/licenses/BSD.txt
  10. #
  11. # Thanks for using Enthought open source!
  12. #
  13. # Author: Martin Chilvers
  14. # Date: 07/18/2007
  15. #
  16. #-------------------------------------------------------------------------------
  17. """ An extension to PyProtocols to simplify the declaration of adapters.
  18. """
  19. #-------------------------------------------------------------------------------
  20. # Imports:
  21. #-------------------------------------------------------------------------------
  22. from __future__ import absolute_import
  23. # Standard library imports:
  24. import weakref
  25. # Traits imports:
  26. from .has_traits import HasTraits
  27. from .trait_types import Any, Bool, Expression
  28. # PyProtocols imports:
  29. from .protocols.api import addClassAdvisor, declareAdapter, declareImplementation, Protocol
  30. #-------------------------------------------------------------------------------
  31. # 'Adapter' class:
  32. #-------------------------------------------------------------------------------
  33. class Adapter ( HasTraits ):
  34. """ The base class for all traits adapters.
  35. In Traits, an *adapter* is a special type of class whose role is to
  36. transform some type of object which does not implement a specific interface,
  37. or set of interfaces, into one that does.
  38. This class is provided as a convenience. If you subclass this class, the
  39. only things you need to add to the subclass definition are:
  40. * An implements() function call declaring which interfaces the adapter
  41. class implements on behalf of the object is is adapting.
  42. * A declaration for the **adaptee** trait, usually as an Instance of
  43. a particular class.
  44. * The actual implementations of the interfaces declared in the
  45. implements() call. Usually the implementation code is written in
  46. terms of the **adaptee** trait.
  47. """
  48. #-- Trait Definitions ------------------------------------------------------
  49. # The object that is being adapted.
  50. adaptee = Any
  51. #-- Constructor ------------------------------------------------------------
  52. def __init__ ( self, adaptee ):
  53. """ Constructor.
  54. We have to declare an explicit constructor because adapters are
  55. created by PyProtocols itself, which knows nothing about traits.
  56. """
  57. super( Adapter, self ).__init__()
  58. self.adaptee = adaptee
  59. #-------------------------------------------------------------------------------
  60. # 'DefaultAdapterFactory' class:
  61. #-------------------------------------------------------------------------------
  62. class DefaultAdapterFactory ( HasTraits ):
  63. """ An adapter factory for producing cached or categorized adapters.
  64. """
  65. #-- 'DefaultAdapterFactory' Interface --------------------------------------
  66. # The adapter class that this factory creates instances of
  67. klass = Any
  68. # Does the factory generate cached adapters?
  69. # If an adapter is cached then the factory will produce at most one
  70. # adapter per instance.
  71. cached = Bool( False )
  72. # An expression that is used to select which instances of a particular
  73. # type can be adapted by this factory.
  74. #
  75. # The expression is evaluated in a namespace that contains a single name
  76. # 'adaptee', which is bound to the object that this factory is attempting
  77. # to adapt (e.g. 'adaptee.is_folder').
  78. when = Expression
  79. #-- Private Interface ------------------------------------------------------
  80. # If this is a cached adapter factory, then this mapping will contain
  81. # the adapters keyed by weak references to the adapted objects.
  82. _adapters = Any
  83. #-------------------------------------------------------------------------------
  84. # 'IAdapterFactory' interface:
  85. #-------------------------------------------------------------------------------
  86. def __call__ ( self, object ):
  87. """ Creates an adapter for the specified object.
  88. Returns **None** if the factory cannot perform the required
  89. adaptation.
  90. """
  91. namespace = { 'adaptee': object }
  92. if eval( self.when_, namespace, namespace ):
  93. if self.cached:
  94. adapter = self._adapters.get( object )
  95. if adapter is None:
  96. self._adapters[ object ] = adapter = self.klass( object )
  97. return adapter
  98. return self.klass( object )
  99. return None
  100. #---------------------------------------------------------------------------
  101. # Private interface:
  102. #---------------------------------------------------------------------------
  103. def __adapters_default ( self ):
  104. """ Trait initializer.
  105. """
  106. return weakref.WeakKeyDictionary()
  107. #-------------------------------------------------------------------------------
  108. # 'adapts' function:
  109. #-------------------------------------------------------------------------------
  110. def adapts ( from_, to, extra = None, factory = None, cached = False,
  111. when = '' ):
  112. """ A class advisor for declaring adapters.
  113. Parameters
  114. ----------
  115. ``from_`` : type or interface
  116. What the adapter adapts *from*, or a list of such types or interfaces
  117. (the '_' suffix is used because 'from' is a Python keyword).
  118. to : type or interface
  119. What the adapter adapts *to*, or a list of such types or interfaces.
  120. factory : callable
  121. An (optional) factory for actually creating the adapters. This is
  122. any callable that takes a single argument which is the object to
  123. be adapted. The factory should return an adapter if it can
  124. perform the adaptation and **None** if it cannot.
  125. The following arguments are ignored if *factory* is specified:
  126. cached : Boolean
  127. Should the adapters be cached? If an adapter is cached, then the
  128. factory will produce at most one adapter per instance.
  129. when : A Python expression
  130. Selects which instances of a particular type can be adapted by this
  131. factory. The expression is evaluated in a namespace that contains a
  132. single name *adaptee*, which is bound to the object to be adapted
  133. (e.g., 'adaptee.is_folder').
  134. """
  135. if extra is not None:
  136. adapter, from_, to = from_, to, extra
  137. else:
  138. adapter = None
  139. def callback ( klass ):
  140. """ Called when the class has been created. """
  141. # What the adapter adapts from:
  142. if type( from_ ) is not list:
  143. for_items = [ from_ ]
  144. else:
  145. for_items = from_
  146. # The things we adapt from have to be split into two lists for
  147. # PyProtocols, one containing Python types (i.e. classes) and one
  148. # containing protocols (i.e. interfaces):
  149. for_types = []
  150. for_protocols = []
  151. for item in for_items:
  152. if isinstance( item, Protocol ):
  153. for_protocols.append( item )
  154. else:
  155. for_types.append( item )
  156. # What the adapter adapts to:
  157. if type( to ) is not list:
  158. provides = [ to ]
  159. else:
  160. provides = to
  161. # Tell PyProtocols that the adapter class implements the protocols that
  162. # it adapts to:
  163. declareImplementation( klass, instancesProvide = provides )
  164. # If a factory was specified then use it:
  165. if factory is not None:
  166. f = factory
  167. # If the adapter is cached or has a 'when' expression then create a
  168. # default factory:
  169. elif cached or (when != ''):
  170. f = DefaultAdapterFactory( klass = klass,
  171. cached = cached,
  172. when = when or 'True' )
  173. # Otherwise, just use the adapter class itself:
  174. else:
  175. f = klass
  176. # Tell PyProtocols about the factory:
  177. declareAdapter( f, provides, forProtocols = for_protocols,
  178. forTypes = for_types )
  179. return klass
  180. if adapter is not None:
  181. callback( adapter )
  182. else:
  183. addClassAdvisor( callback )