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__)