0001"""
0002Declarative objects for FormEncode.
0003
0004Declarative objects have a simple protocol: you can use classes in
0005lieu of instances and they are equivalent, and any keyword arguments
0006you give to the constructor will override those instance variables.
0007(So if a class is received, we'll simply instantiate an instance with
0008no arguments).
0009
0010You can provide a variable __unpackargs__ (a list of strings), and if
0011the constructor is called with non-keyword arguments they will be
0012interpreted as the given keyword arguments.
0013
0014If __unpackargs__ is ('*', name), then all the arguments will be put
0015in a variable by that name.
0016
0017Also, you can define a __classinit__(cls, new_attrs) method, which
0018will be called when the class is created (including subclasses).
0019"""
0020
0021from __future__ import generators
0022
0023import copy
0024import new
0025
0026try:
0027 import itertools
0028 counter = itertools.count()
0029except ImportError:
0030 def _counter():
0031 i = 0
0032 while 1:
0033 i += 1
0034 yield i
0035 counter = _counter()
0036
0037class classinstancemethod(object):
0038 """
0039 Acts like a class method when called from a class, like an
0040 instance method when called by an instance. The method should
0041 take two arguments, 'self' and 'cls'; one of these will be None
0042 depending on how the method was called.
0043 """
0044
0045 def __init__(self, func):
0046 self.func = func
0047
0048 def __get__(self, obj, type=None):
0049 return _methodwrapper(self.func, obj=obj, type=type)
0050
0051class _methodwrapper(object):
0052
0053 def __init__(self, func, obj, type):
0054 self.func = func
0055 self.obj = obj
0056 self.type = type
0057
0058 def __call__(self, *args, **kw):
0059 assert not kw.has_key('self') and not kw.has_key('cls'), (
0060 "You cannot use 'self' or 'cls' arguments to a "
0061 "classinstancemethod")
0062 return self.func(*((self.obj, self.type) + args), **kw)
0063
0064 def __repr__(self):
0065 if self.obj is None:
0066 return ('<bound class method %s.%s>'
0067 % (self.type.__name__, self.func.func_name))
0068 else:
0069 return ('<bound method %s.%s of %r>'
0070 % (self.type.__name__, self.func.func_name, self.obj))
0071
0072
0073class DeclarativeMeta(type):
0074
0075 def __new__(meta, class_name, bases, new_attrs):
0076 cls = type.__new__(meta, class_name, bases, new_attrs)
0077 for name in cls.__mutableattributes__:
0078 setattr(cls, name, copy.copy(getattr(cls, name)))
0079 cls.declarative_count = counter.next()
0080 if (new_attrs.has_key('__classinit__')
0081 and not isinstance(cls.__classinit__, staticmethod)):
0082 setattr(cls, '__classinit__',
0083 staticmethod(cls.__classinit__.im_func))
0084 cls.__classinit__(cls, new_attrs)
0085 names = getattr(cls, '__singletonmethods__', None)
0086 if names:
0087 for name in names:
0088 meth = cls.__dict__.get(name)
0089 if meth and not isinstance(meth, singletonmethod):
0090 setattr(cls, name, singletonmethod(meth))
0091 return cls
0092
0093class singletonmethod(object):
0094 """
0095 For Declarative subclasses, this decorator will call the method
0096 on the cls.singleton() object if called as a class method (or
0097 as normal if called as an instance method).
0098 """
0099
0100 def __init__(self, func):
0101 self.func = func
0102
0103 def __get__(self, obj, type=None):
0104 if obj is None:
0105 obj = type.singleton()
0106 if type is None:
0107 type = obj.__class__
0108 return new.instancemethod(self.func, obj, type)
0109
0110class Declarative(object):
0111
0112 __unpackargs__ = ()
0113
0114 __mutableattributes__ = ()
0115
0116 __metaclass__ = DeclarativeMeta
0117
0118 __singletonmethods__ = ()
0119
0120 def __classinit__(cls, new_attrs):
0121 pass
0122
0123 def __init__(self, *args, **kw):
0124 if self.__unpackargs__ and self.__unpackargs__[0] == '*':
0125 assert len(self.__unpackargs__) == 2, "When using __unpackargs__ = ('*', varname), you must only provide a single variable name (you gave %r)" % self.__unpackargs__
0127 name = self.__unpackargs__[1]
0128 if kw.has_key(name):
0129 raise TypeError(
0130 "keyword parameter '%s' was given by position and name"
0131 % name)
0132 kw[name] = args
0133 else:
0134 if len(args) > len(self.__unpackargs__):
0135 raise TypeError(
0136 '%s() takes at most %i arguments (%i given)'
0137 % (self.__class__.__name__,
0138 len(self.__unpackargs__),
0139 len(args)))
0140 for name, arg in zip(self.__unpackargs__, args):
0141 if kw.has_key(name):
0142 raise TypeError(
0143 "keyword parameter '%s' was given by position and name"
0144 % name)
0145 kw[name] = arg
0146 for name in self.__mutableattributes__:
0147 if not kw.has_key(name):
0148 setattr(self, name, copy.copy(getattr(self, name)))
0149 for name, value in kw.items():
0150 setattr(self, name, value)
0151 if not kw.has_key('declarative_count'):
0152 self.declarative_count = counter.next()
0153 self.__initargs__(kw)
0154
0155 def __initargs__(self, new_attrs):
0156 pass
0157
0158 def __call__(self, *args, **kw):
0159 current = self.__dict__.copy()
0160 current.update(kw)
0161 return self.__class__(*args, **current)
0162
0163 def singleton(cls):
0164 name = '_%s__singleton' % cls.__name__
0165 if not hasattr(cls, name):
0166 setattr(cls, name, cls(declarative_count=cls.declarative_count))
0167 return getattr(cls, name)
0168 singleton = classmethod(singleton)
0169
0170 def __sourcerepr__(self, source, binding=None):
0171 if binding and len(self.__dict__) > 3:
0172 return self._source_repr_class(source, binding=binding)
0173 else:
0174 vals = self.__dict__.copy()
0175 if vals.has_key('declarative_count'):
0176 del vals['declarative_count']
0177 args = []
0178 if (self.__unpackargs__ and self.__unpackargs__[0] == '*'
0179 and vals.has_key(self.__unpackargs__[1])):
0180 v = vals[self.__unpackargs__[1]]
0181 if isinstance(v, (list, int)):
0182 args.extend(map(source.makeRepr, v))
0183 del v[self.__unpackargs__[1]]
0184 for name in self.__unpackargs__:
0185 if vals.has_key(name):
0186 args.append(source.makeRepr(vals[name]))
0187 del vals[name]
0188 else:
0189 break
0190 args.extend(['%s=%s' % (name, source.makeRepr(value))
0191 for (name, value) in vals.items()])
0192 return '%s(%s)' % (self.__class__.__name__,
0193 ', '.join(args))
0194
0195 def _source_repr_class(self, source, binding=None):
0196 d = self.__dict__.copy()
0197 if d.has_key('declarative_count'):
0198 del d['declarative_count']
0199 return source.makeClass(self, binding, d,
0200 (self.__class__,))
0201
0202 def __classsourcerepr__(cls, source, binding=None):
0203 d = cls.__dict__.copy()
0204 del d['declarative_count']
0205 return source.makeClass(cls, binding or cls.__name__, d,
0206 cls.__bases__)
0207 __classsourcerepr__ = classmethod(__classsourcerepr__)
0208
0209 def __repr__(self, cls):
0210 if self:
0211 name = '%s object' % self.__class__.__name__
0212 v = self.__dict__.copy()
0213 else:
0214 name = '%s class' % cls.__name__
0215 v = cls.__dict__.copy()
0216 if v.has_key('declarative_count'):
0217 name = '%s %i' % (name, v['declarative_count'])
0218 del v['declarative_count']
0219 names = v.keys()
0220 args = []
0221 for n in self._repr_vars(names):
0222 args.append('%s=%r' % (n, v[n]))
0223 if not args:
0224 return '<%s>' % name
0225 else:
0226 return '<%s %s>' % (name, ' '.join(args))
0227
0228 def _repr_vars(dictNames):
0229 names = [n for n in dictNames
0230 if not n.startswith('_')
0231 and n != 'declarative_count']
0232 names.sort()
0233 return names
0234 _repr_vars = staticmethod(_repr_vars)
0235
0236 __repr__ = classinstancemethod(__repr__)