text: # -*- test-case-name: epsilon.test.test_descriptor -*-
  
  """
  Provides an 'attribute' class for one-use descriptors.
  """
  
  attribute = None
  
  class _MetaAttribute(type):
      def __new__(meta, name, bases, dict):
          # for reals, yo.
          for kw in ['get', 'set', 'delete']:
              if kw in dict:
                  dict[kw] = staticmethod(dict[kw])
          secretClass = type.__new__(meta, name, bases, dict)
          if attribute is None:
              return secretClass
          return secretClass()
  
  class attribute(object):
      """
      Convenience class for providing one-shot descriptors, similar to
      'property'.  For example:
  
          >>> from epsilon.descriptor import attribute
          >>> class Dynamo(object):
          ...  class dynamic(attribute):
          ...   def get(self):
          ...    self.dynCount += 1
          ...    return self.dynCount
          ...   def set(self, value):
          ...    self.dynCount += value
          ...  dynCount = 0
          ...
          >>> d = Dynamo()
          >>> d.dynamic
          1
          >>> d.dynamic
          2
          >>> d.dynamic = 6
          >>> d.dynamic
          9
          >>> d.dynamic
          10
          >>> del d.dynamic
          Traceback (most recent call last):
              ...
          AttributeError: attribute cannot be removed
      """
  
      __metaclass__ = _MetaAttribute
  
      def __get__(self, oself, type):
          """
          Private implementation of descriptor interface.
          """
          if oself is None:
              return self
          return self.get(oself)
  
      def __set__(self, oself, value):
          """
          Private implementation of descriptor interface.
          """
          return self.set(oself, value)
  
      def __delete__(self, oself):
          """
          Private implementation of descriptor interface.
          """
          return self.delete(oself)
  
      def set(self, value):
          """
          Implement this method to provide attribute setting.  Default behavior
          is that attributes are not settable.
          """
          raise AttributeError('read only attribute')
  
      def get(self):
          """
          Implement this method to provide attribute retrieval.  Default behavior
          is that unset attributes do not have any value.
          """
          raise AttributeError('attribute has no value')
  
      def delete(self):
          """
          Implement this method to provide attribute deletion.  Default behavior
          is that attributes cannot be deleted.
          """
          raise AttributeError('attribute cannot be removed')
  
  
  
  def requiredAttribute(requiredAttributeName):
      """
      Utility for defining attributes on base classes/mixins which require their
      values to be supplied by their derived classes.  C{None} is a common, but
      almost never suitable default value for these kinds of attributes, as it
      may cause operations in the derived class to fail silently in peculiar
      ways.  If a C{requiredAttribute} is accessed before having its value
      changed, a C{AttributeError} will be raised with a helpful error message.
  
      @param requiredAttributeName: The name of the required attribute.
      @type requiredAttributeName: C{str}
  
      Example:
          >>> from epsilon.descriptor import requiredAttribute
          ...
          >>> class FooTestMixin:
          ...  expectedResult = requiredAttribute('expectedResult')
          ...
          >>> class BrokenFooTestCase(TestCase, FooTestMixin):
          ...  pass
          ...
          >>> brokenFoo = BrokenFooTestCase()
          >>> print brokenFoo.expectedResult
          Traceback (most recent call last):
              ...
          AttributeError: Required attribute 'expectedResult' has not been
                          changed from its default value on '<BrokenFooTestCase
                          instance>'.
          ...
          >>> class WorkingFooTestCase(TestCase, FooTestMixin):
          ...  expectedResult = 7
          ...
          >>> workingFoo = WorkingFooTestCase()
          >>> print workingFoo.expectedResult
          ... 7
          >>>
      """
      class RequiredAttribute(attribute):
          def get(self):
              if requiredAttributeName not in self.__dict__:
                  raise AttributeError(
                      ('Required attribute %r has not been changed'
                       ' from its default value on %r' % (
                              requiredAttributeName, self)))
              return self.__dict__[requiredAttributeName]
          def set(self, value):
              self.__dict__[requiredAttributeName] = value
      return RequiredAttribute
  
  
  
  __all__ = ['attribute', 'requiredAttribute']
  