formencode.declarative
Covered: 67 lines
Missed: 133 lines
Skipped 38 lines
Percent: 33 %
  1
"""
  2
Declarative objects for FormEncode.
  4
Declarative objects have a simple protocol: you can use classes in
  5
lieu of instances and they are equivalent, and any keyword arguments
  6
you give to the constructor will override those instance variables.
  7
(So if a class is received, we'll simply instantiate an instance with
  8
no arguments).
 10
You can provide a variable __unpackargs__ (a list of strings), and if
 11
the constructor is called with non-keyword arguments they will be
 12
interpreted as the given keyword arguments.
 14
If __unpackargs__ is ('*', name), then all the arguments will be put
 15
in a variable by that name.
 17
Also, you can define a __classinit__(cls, new_attrs) method, which
 18
will be called when the class is created (including subclasses).
 19
"""
 21
from __future__ import generators
 23
import copy
 24
import new
 26
try:
 27
    import itertools
 28
    counter = itertools.count()
 29
except ImportError:
 30
    def _counter():
 31
        i = 0
 32
        while 1:
 33
            i += 1
 34
            yield i
 35
    counter = _counter()
 37
class classinstancemethod(object):
 38
    """
 39
    Acts like a class method when called from a class, like an
 40
    instance method when called by an instance.  The method should
 41
    take two arguments, 'self' and 'cls'; one of these will be None
 42
    depending on how the method was called.
 43
    """
 45
    def __init__(self, func):
 46
        self.func = func
 48
    def __get__(self, obj, type=None):
 49
        return _methodwrapper(self.func, obj=obj, type=type)
 51
class _methodwrapper(object):
 53
    def __init__(self, func, obj, type):
 54
        self.func = func
 55
        self.obj = obj
 56
        self.type = type
 58
    def __call__(self, *args, **kw):
 59
        assert not kw.has_key('self') and not kw.has_key('cls'), (
 60
            "You cannot use 'self' or 'cls' arguments to a "
 61
            "classinstancemethod")
 62
        return self.func(*((self.obj, self.type) + args), **kw)
 64
    def __repr__(self):
 65
        if self.obj is None:
 66
            return ('<bound class method %s.%s>'
 67
                    % (self.type.__name__, self.func.func_name))
 68
        else:
 69
            return ('<bound method %s.%s of %r>'
 70
                    % (self.type.__name__, self.func.func_name, self.obj))
 73
class DeclarativeMeta(type):
 75
    def __new__(meta, class_name, bases, new_attrs):
 76
        cls = type.__new__(meta, class_name, bases, new_attrs)
 77
        for name in cls.__mutableattributes__:
 78
            setattr(cls, name, copy.copy(getattr(cls, name)))
 79
        cls.declarative_count = counter.next()
 80
        if (new_attrs.has_key('__classinit__')
 81
            and not isinstance(cls.__classinit__, staticmethod)):
 82
            setattr(cls, '__classinit__',
 83
                    staticmethod(cls.__classinit__.im_func))
 84
        cls.__classinit__(cls, new_attrs)
 85
        names = getattr(cls, '__singletonmethods__', None)
 86
        if names:
 87
            for name in names:
 88
                meth = cls.__dict__.get(name)
 89
                if meth and not isinstance(meth, singletonmethod):
 90
                    setattr(cls, name, singletonmethod(meth))
 91
        return cls
 93
class singletonmethod(object):
 94
    """
 95
    For Declarative subclasses, this decorator will call the method
 96
    on the cls.singleton() object if called as a class method (or
 97
    as normal if called as an instance method).
 98
    """
100
    def __init__(self, func):
101
        self.func = func
103
    def __get__(self, obj, type=None):
104
        if obj is None:
105
            obj = type.singleton()
106
        if type is None:
107
            type = obj.__class__
108
        return new.instancemethod(self.func, obj, type)
110
class Declarative(object):
112
    __unpackargs__ = ()
114
    __mutableattributes__ = ()
116
    __metaclass__ = DeclarativeMeta
118
    __singletonmethods__ = ()
120
    def __classinit__(cls, new_attrs):
121
        pass
123
    def __init__(self, *args, **kw):
124
        if self.__unpackargs__ and self.__unpackargs__[0] == '*':
125
            assert len(self.__unpackargs__) == 2, \
126
                   "When using __unpackargs__ = ('*', varname), you must only provide a single variable name (you gave %r)" % self.__unpackargs__
127
            name = self.__unpackargs__[1]
128
            if kw.has_key(name):
129
                raise TypeError(
130
                    "keyword parameter '%s' was given by position and name"
131
                    % name)
132
            kw[name] = args
133
        else:
134
            if len(args) > len(self.__unpackargs__):
135
                raise TypeError(
136
                    '%s() takes at most %i arguments (%i given)'
137
                    % (self.__class__.__name__,
138
                       len(self.__unpackargs__),
139
                       len(args)))
140
            for name, arg in zip(self.__unpackargs__, args):
141
                if kw.has_key(name):
142
                    raise TypeError(
143
                        "keyword parameter '%s' was given by position and name"
144
                        % name)
145
                kw[name] = arg
146
        for name in self.__mutableattributes__:
147
            if not kw.has_key(name):
148
                setattr(self, name, copy.copy(getattr(self, name)))
149
        for name, value in kw.items():
150
            setattr(self, name, value)
151
        if not kw.has_key('declarative_count'):
152
            self.declarative_count = counter.next()
153
        self.__initargs__(kw)
155
    def __initargs__(self, new_attrs):
156
        pass
158
    def __call__(self, *args, **kw):
159
        current = self.__dict__.copy()
160
        current.update(kw)
161
        return self.__class__(*args, **current)
163
    def singleton(cls):
164
        name = '_%s__singleton' % cls.__name__
165
        if not hasattr(cls, name):
166
            setattr(cls, name, cls(declarative_count=cls.declarative_count))
167
        return getattr(cls, name)
168
    singleton = classmethod(singleton)
170
    def __sourcerepr__(self, source, binding=None):
171
        if binding and len(self.__dict__) > 3:
172
            return self._source_repr_class(source, binding=binding)
173
        else:
174
            vals = self.__dict__.copy()
175
            if vals.has_key('declarative_count'):
176
                del vals['declarative_count']
177
            args = []
178
            if (self.__unpackargs__ and self.__unpackargs__[0] == '*'
179
                and vals.has_key(self.__unpackargs__[1])):
180
                v = vals[self.__unpackargs__[1]]
181
                if isinstance(v, (list, int)):
182
                    args.extend(map(source.makeRepr, v))
183
                    del v[self.__unpackargs__[1]]
184
            for name in self.__unpackargs__:
185
                if vals.has_key(name):
186
                    args.append(source.makeRepr(vals[name]))
187
                    del vals[name]
188
                else:
189
                    break
190
            args.extend(['%s=%s' % (name, source.makeRepr(value))
191
                         for (name, value) in vals.items()])
192
            return '%s(%s)' % (self.__class__.__name__,
193
                               ', '.join(args))
195
    def _source_repr_class(self, source, binding=None):
196
        d = self.__dict__.copy()
197
        if d.has_key('declarative_count'):
198
            del d['declarative_count']
199
        return source.makeClass(self, binding, d,
200
                                (self.__class__,))
202
    def __classsourcerepr__(cls, source, binding=None):
203
        d = cls.__dict__.copy()
204
        del d['declarative_count']
205
        return source.makeClass(cls, binding or cls.__name__, d,
206
                                cls.__bases__)
207
    __classsourcerepr__ = classmethod(__classsourcerepr__)
209
    def __repr__(self, cls):
210
        if self:
211
            name = '%s object' % self.__class__.__name__
212
            v = self.__dict__.copy()
213
        else:
214
            name = '%s class' % cls.__name__
215
            v = cls.__dict__.copy()
216
        if v.has_key('declarative_count'):
217
            name = '%s %i' % (name, v['declarative_count'])
218
            del v['declarative_count']
219
        names = v.keys()
220
        args = []
221
        for n in self._repr_vars(names):
222
            args.append('%s=%r' % (n, v[n]))
223
        if not args:
224
            return '<%s>' % name
225
        else:
226
            return '<%s %s>' % (name, ' '.join(args))
228
    def _repr_vars(dictNames):
229
        names = [n for n in dictNames
230
                 if not n.startswith('_')
231
                 and n != 'declarative_count']
232
        names.sort()
233
        return names
234
    _repr_vars = staticmethod(_repr_vars)
236
    __repr__ = classinstancemethod(__repr__)