""" Declarative objects for FormEncode. Declarative objects have a simple protocol: you can use classes in lieu of instances and they are equivalent, and any keyword arguments you give to the constructor will override those instance variables. (So if a class is received, we'll simply instantiate an instance with no arguments). You can provide a variable __unpackargs__ (a list of strings), and if the constructor is called with non-keyword arguments they will be interpreted as the given keyword arguments. If __unpackargs__ is ('*', name), then all the arguments will be put in a variable by that name. """ import copy try: from cStringIO import StringIO except ImportError: from StringIO import StringIO _declarativeCount = 0 def _getCount(): global _declarativeCount last, _declarativeCount = _declarativeCount, _declarativeCount+1 return last class classinstancemethod(object): """ Acts like a class method when called from a class, like an instance method when called by an instance. The method should take two arguments, 'self' and 'cls'; one of these will be None depending on how the method was called. """ def __init__(self, func): self.func = func def __get__(self, obj, type=None): return _methodwrapper(self.func, obj=obj, type=type) class _methodwrapper(object): def __init__(self, func, obj, type): self.func = func self.obj = obj self.type = type def __call__(self, *args, **kw): assert not kw.has_key('self') and not kw.has_key('cls'), "You cannot use 'self' or 'cls' arguments to a classinstancemethod" #kw['self'] = self.obj #kw['cls'] = self.type #print self.func, args, kw return self.func(*((self.obj, self.type) + args), **kw) def __repr__(self): if self.obj is None: return '' % (self.type.__name__, self.func.func_name) else: return '' % (self.type.__name__, self.func.func_name, self.obj) class DeclarativeMeta(type): def __new__(meta, className, bases, d): global _declarativeCount cls = type.__new__(meta, className, bases, d) #if d.has_key('__classinit__'): # cls.__classinit__ = staticmethod(cls.__classinit__) cls.declarativeCount = _getCount() cls.__classinit__.im_func(cls) return cls class Declarative(object): __unpackargs__ = () __mutableattributes__ = () __metaclass__ = DeclarativeMeta def __classinit__(cls): pass def __init__(self, *args, **kw): if self.__unpackargs__ and self.__unpackargs__[0] == '*': assert len(self.__unpackargs__) == 2, \ "When using __unpackargs__ = ('*', varname), you must only provide a single variable name (you gave %r)" % self.__unpackargs__ name = self.__unpackargs__[1] if kw.has_key(name): raise TypeError( "keyword parameter '%s' was given by position and name" % name) kw[name] = args else: if len(args) > len(self.__unpackargs__): raise TypeError( '%s() takes at most %i arguments (%i given)' % (self.__class__.__name__, len(self.__unpackargs__), len(args))) for name, arg in zip(self.__unpackargs__, args): if kw.has_key(name): raise TypeError( "keyword parameter '%s' was given by position and name" % name) kw[name] = arg if kw.has_key('__alsocopy'): for name, value in kw['__alsocopy'].items(): if not kw.has_key(name): if name in self.__mutableattributes__: value = copy.copy(value) kw[name] = value del kw['__alsocopy'] for name, value in kw.items(): setattr(self, name, value) if not kw.has_key('declarativeCount'): self.declarativeCount = _getCount() def __call__(self, *args, **kw): kw['__alsocopy'] = self.__dict__ return self.__class__(*args, **kw) def singleton(cls): name = '_%s__singleton' % cls.__name__ if not hasattr(cls, name): setattr(cls, name, cls(declarativeCount=cls.declarativeCount)) return getattr(cls, name) singleton = classmethod(singleton) def __sourcerepr__(self, source, binding=None): if binding and len(self.__dict__) > 3: return self._sourceReprClass(source, binding=binding) else: vals = self.__dict__.copy() if vals.has_key('declarativeCount'): del vals['declarativeCount'] args = [] if (self.__unpackargs__ and self.__unpackargs__[0] == '*' and vals.has_key(self.__unpackargs__[1])): v = vals[self.__unpackargs__[1]] if isinstance(v, (list, int)): args.extend(map(source.makeRepr, v)) del v[self.__unpackargs__[1]] for name in self.__unpackargs__: if vals.has_key(name): args.append(source.makeRepr(vals[name])) del vals[name] else: break args.extend(['%s=%s' % (name, source.makeRepr(value)) for (name, value) in vals.items()]) return '%s(%s)' % (self.__class__.__name__, ', '.join(args)) def _sourceReprClass(self, source, binding=None): d = self.__dict__.copy() if d.has_key('declarativeCount'): del d['declarativeCount'] return source.makeClass(self, binding, d, (self.__class__,)) def __classsourcerepr__(cls, source, binding=None): d = cls.__dict__.copy() del d['declarativeCount'] return source.makeClass(cls, binding or cls.__name__, d, cls.__bases__) __classsourcerepr__ = classmethod(__classsourcerepr__) def __repr__(self, cls): if self: name = '%s object' % self.__class__.__name__ v = self.__dict__.copy() else: name = '%s class' % cls.__name__ v = cls.__dict__.copy() if v.has_key('declarativeCount'): name = '%s %i' % (name, v['declarativeCount']) del v['declarativeCount'] names = v.keys() args = [] for n in self._reprVars(names): args.append('%s=%r' % (n, v[n])) if not args: return '<%s>' % name else: return '<%s %s>' % (name, ' '.join(args)) def _reprVars(dictNames): names = [n for n in dictNames if not n.startswith('_') and n != 'declarativeCount'] names.sort() return names _reprVars = staticmethod(_reprVars) __repr__ = classinstancemethod(__repr__) def getinstance(v): """ Returns an instance of v is v is a class, else v. """ if isinstance(v, type): return v() else: return v