~divmod-dev/divmod.org/trunk

« back to all changes in this revision

Viewing changes to Epsilon/epsilon/descriptor.py

  • Committer: Jean-Paul Calderone
  • Date: 2014-06-29 20:33:04 UTC
  • mfrom: (2749.1.1 remove-epsilon-1325289)
  • Revision ID: exarkun@twistedmatrix.com-20140629203304-gdkmbwl1suei4m97
mergeĀ lp:~exarkun/divmod.org/remove-epsilon-1325289

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- test-case-name: epsilon.test.test_descriptor -*-
2
 
 
3
 
"""
4
 
Provides an 'attribute' class for one-use descriptors.
5
 
"""
6
 
 
7
 
attribute = None
8
 
 
9
 
class _MetaAttribute(type):
10
 
    def __new__(meta, name, bases, dict):
11
 
        # for reals, yo.
12
 
        for kw in ['get', 'set', 'delete']:
13
 
            if kw in dict:
14
 
                dict[kw] = staticmethod(dict[kw])
15
 
        secretClass = type.__new__(meta, name, bases, dict)
16
 
        if attribute is None:
17
 
            return secretClass
18
 
        return secretClass()
19
 
 
20
 
class attribute(object):
21
 
    """
22
 
    Convenience class for providing one-shot descriptors, similar to
23
 
    'property'.  For example:
24
 
 
25
 
        >>> from epsilon.descriptor import attribute
26
 
        >>> class Dynamo(object):
27
 
        ...  class dynamic(attribute):
28
 
        ...   def get(self):
29
 
        ...    self.dynCount += 1
30
 
        ...    return self.dynCount
31
 
        ...   def set(self, value):
32
 
        ...    self.dynCount += value
33
 
        ...  dynCount = 0
34
 
        ...
35
 
        >>> d = Dynamo()
36
 
        >>> d.dynamic
37
 
        1
38
 
        >>> d.dynamic
39
 
        2
40
 
        >>> d.dynamic = 6
41
 
        >>> d.dynamic
42
 
        9
43
 
        >>> d.dynamic
44
 
        10
45
 
        >>> del d.dynamic
46
 
        Traceback (most recent call last):
47
 
            ...
48
 
        AttributeError: attribute cannot be removed
49
 
    """
50
 
 
51
 
    __metaclass__ = _MetaAttribute
52
 
 
53
 
    def __get__(self, oself, type):
54
 
        """
55
 
        Private implementation of descriptor interface.
56
 
        """
57
 
        if oself is None:
58
 
            return self
59
 
        return self.get(oself)
60
 
 
61
 
    def __set__(self, oself, value):
62
 
        """
63
 
        Private implementation of descriptor interface.
64
 
        """
65
 
        return self.set(oself, value)
66
 
 
67
 
    def __delete__(self, oself):
68
 
        """
69
 
        Private implementation of descriptor interface.
70
 
        """
71
 
        return self.delete(oself)
72
 
 
73
 
    def set(self, value):
74
 
        """
75
 
        Implement this method to provide attribute setting.  Default behavior
76
 
        is that attributes are not settable.
77
 
        """
78
 
        raise AttributeError('read only attribute')
79
 
 
80
 
    def get(self):
81
 
        """
82
 
        Implement this method to provide attribute retrieval.  Default behavior
83
 
        is that unset attributes do not have any value.
84
 
        """
85
 
        raise AttributeError('attribute has no value')
86
 
 
87
 
    def delete(self):
88
 
        """
89
 
        Implement this method to provide attribute deletion.  Default behavior
90
 
        is that attributes cannot be deleted.
91
 
        """
92
 
        raise AttributeError('attribute cannot be removed')
93
 
 
94
 
 
95
 
 
96
 
def requiredAttribute(requiredAttributeName):
97
 
    """
98
 
    Utility for defining attributes on base classes/mixins which require their
99
 
    values to be supplied by their derived classes.  C{None} is a common, but
100
 
    almost never suitable default value for these kinds of attributes, as it
101
 
    may cause operations in the derived class to fail silently in peculiar
102
 
    ways.  If a C{requiredAttribute} is accessed before having its value
103
 
    changed, a C{AttributeError} will be raised with a helpful error message.
104
 
 
105
 
    @param requiredAttributeName: The name of the required attribute.
106
 
    @type requiredAttributeName: C{str}
107
 
 
108
 
    Example:
109
 
        >>> from epsilon.descriptor import requiredAttribute
110
 
        ...
111
 
        >>> class FooTestMixin:
112
 
        ...  expectedResult = requiredAttribute('expectedResult')
113
 
        ...
114
 
        >>> class BrokenFooTestCase(TestCase, FooTestMixin):
115
 
        ...  pass
116
 
        ...
117
 
        >>> brokenFoo = BrokenFooTestCase()
118
 
        >>> print brokenFoo.expectedResult
119
 
        Traceback (most recent call last):
120
 
            ...
121
 
        AttributeError: Required attribute 'expectedResult' has not been
122
 
                        changed from its default value on '<BrokenFooTestCase
123
 
                        instance>'.
124
 
        ...
125
 
        >>> class WorkingFooTestCase(TestCase, FooTestMixin):
126
 
        ...  expectedResult = 7
127
 
        ...
128
 
        >>> workingFoo = WorkingFooTestCase()
129
 
        >>> print workingFoo.expectedResult
130
 
        ... 7
131
 
        >>>
132
 
    """
133
 
    class RequiredAttribute(attribute):
134
 
        def get(self):
135
 
            if requiredAttributeName not in self.__dict__:
136
 
                raise AttributeError(
137
 
                    ('Required attribute %r has not been changed'
138
 
                     ' from its default value on %r' % (
139
 
                            requiredAttributeName, self)))
140
 
            return self.__dict__[requiredAttributeName]
141
 
        def set(self, value):
142
 
            self.__dict__[requiredAttributeName] = value
143
 
    return RequiredAttribute
144
 
 
145
 
 
146
 
 
147
 
__all__ = ['attribute', 'requiredAttribute']