import operator from .compat import PY2 from .compat import PY3 from .compat import with_metaclass from .utils import cached_property from .utils import identity def make_proxy_method(code): def proxy_wrapper(self, *args): return code(self.__wrapped__, *args) return proxy_wrapper class _ProxyMethods(object): # We use properties to override the values of __module__ and # __doc__. If we add these in ObjectProxy, the derived class # __dict__ will still be setup to have string variants of these # attributes and the rules of descriptors means that they appear to # take precedence over the properties in the base class. To avoid # that, we copy the properties into the derived class type itself # via a meta class. In that way the properties will always take # precedence. @property def __module__(self): return self.__wrapped__.__module__ @__module__.setter def __module__(self, value): self.__wrapped__.__module__ = value @property def __doc__(self): return self.__wrapped__.__doc__ @__doc__.setter def __doc__(self, value): self.__wrapped__.__doc__ = value # Need to also propagate the special __weakref__ attribute for case # where decorating classes which will define this. If do not define # it and use a function like inspect.getmembers() on a decorator # class it will fail. This can't be in the derived classes. @property def __weakref__(self): return self.__wrapped__.__weakref__ class _ProxyMetaType(type): def __new__(cls, name, bases, dictionary): # Copy our special properties into the class so that they # always take precedence over attributes of the same name added # during construction of a derived class. This is to save # duplicating the implementation for them in all derived classes. dictionary.update(vars(_ProxyMethods)) dictionary.pop('__dict__') return type.__new__(cls, name, bases, dictionary) class Proxy(with_metaclass(_ProxyMetaType)): __factory__ = None def __init__(self, factory): self.__dict__['__factory__'] = factory @cached_property def __wrapped__(self): self = self.__dict__ if '__factory__' in self: factory = self['__factory__'] return factory() else: raise ValueError("Proxy hasn't been initiated: __factory__ is missing.") __name__ = property(make_proxy_method(operator.attrgetter('__name__'))) __class__ = property(make_proxy_method(operator.attrgetter('__class__'))) __annotations__ = property(make_proxy_method(operator.attrgetter('__anotations__'))) __dir__ = make_proxy_method(dir) __str__ = make_proxy_method(str) if PY3: __bytes__ = make_proxy_method(bytes) def __repr__(self, __getattr__=object.__getattribute__): if '__wrapped__' in self.__dict__: return '<%s at 0x%x wrapping %r at 0x%x with factory %r>' % ( type(self).__name__, id(self), self.__wrapped__, id(self.__wrapped__), self.__factory__ ) else: return '<%s at 0x%x with factory %r>' % ( type(self).__name__, id(self), self.__factory__ ) __reversed__ = make_proxy_method(reversed) if PY3: __round__ = make_proxy_method(round) __lt__ = make_proxy_method(operator.lt) __le__ = make_proxy_method(operator.le) __eq__ = make_proxy_method(operator.eq) __ne__ = make_proxy_method(operator.ne) __gt__ = make_proxy_method(operator.gt) __ge__ = make_proxy_method(operator.ge) __hash__ = make_proxy_method(hash) __nonzero__ = make_proxy_method(bool) __bool__ = make_proxy_method(bool) def __setattr__(self, name, value): if hasattr(type(self), name): self.__dict__[name] = value else: setattr(self.__wrapped__, name, value) def __getattr__(self, name): if name in ('__wrapped__', '__factory__'): raise AttributeError(name) else: return getattr(self.__wrapped__, name) def __delattr__(self, name): if hasattr(type(self), name): del self.__dict__[name] else: delattr(self.__wrapped__, name) __add__ = make_proxy_method(operator.add) __sub__ = make_proxy_method(operator.sub) __mul__ = make_proxy_method(operator.mul) __div__ = make_proxy_method(operator.div if PY2 else operator.truediv) __truediv__ = make_proxy_method(operator.truediv) __floordiv__ = make_proxy_method(operator.floordiv) __mod__ = make_proxy_method(operator.mod) __divmod__ = make_proxy_method(divmod) __pow__ = make_proxy_method(pow) __lshift__ = make_proxy_method(operator.lshift) __rshift__ = make_proxy_method(operator.rshift) __and__ = make_proxy_method(operator.and_) __xor__ = make_proxy_method(operator.xor) __or__ = make_proxy_method(operator.or_) def __radd__(self, other): return other + self.__wrapped__ def __rsub__(self, other): return other - self.__wrapped__ def __rmul__(self, other): return other * self.__wrapped__ def __rdiv__(self, other): return operator.div(other, self.__wrapped__) def __rtruediv__(self, other): return operator.truediv(other, self.__wrapped__) def __rfloordiv__(self, other): return other // self.__wrapped__ def __rmod__(self, other): return other % self.__wrapped__ def __rdivmod__(self, other): return divmod(other, self.__wrapped__) def __rpow__(self, other, *args): return pow(other, self.__wrapped__, *args) def __rlshift__(self, other): return other << self.__wrapped__ def __rrshift__(self, other): return other >> self.__wrapped__ def __rand__(self, other): return other & self.__wrapped__ def __rxor__(self, other): return other ^ self.__wrapped__ def __ror__(self, other): return other | self.__wrapped__ __iadd__ = make_proxy_method(operator.iadd) __isub__ = make_proxy_method(operator.isub) __imul__ = make_proxy_method(operator.imul) __idiv__ = make_proxy_method(operator.idiv if PY2 else operator.itruediv) __itruediv__ = make_proxy_method(operator.itruediv) __ifloordiv__ = make_proxy_method(operator.ifloordiv) __imod__ = make_proxy_method(operator.imod) __ipow__ = make_proxy_method(operator.ipow) __ilshift__ = make_proxy_method(operator.ilshift) __irshift__ = make_proxy_method(operator.irshift) __iand__ = make_proxy_method(operator.iand) __ixor__ = make_proxy_method(operator.ixor) __ior__ = make_proxy_method(operator.ior) __neg__ = make_proxy_method(operator.neg) __pos__ = make_proxy_method(operator.pos) __abs__ = make_proxy_method(operator.abs) __invert__ = make_proxy_method(operator.invert) __int__ = make_proxy_method(int) if PY2: __long__ = make_proxy_method(long) # flake8: noqa __float__ = make_proxy_method(float) __oct__ = make_proxy_method(oct) __hex__ = make_proxy_method(hex) __index__ = make_proxy_method(operator.index) __len__ = make_proxy_method(len) __contains__ = make_proxy_method(operator.contains) __getitem__ = make_proxy_method(operator.getitem) __setitem__ = make_proxy_method(operator.setitem) __delitem__ = make_proxy_method(operator.delitem) if PY2: __getslice__ = make_proxy_method(operator.getslice) __setslice__ = make_proxy_method(operator.setslice) __delslice__ = make_proxy_method(operator.delslice) def __enter__(self): return self.__wrapped__.__enter__() def __exit__(self, *args, **kwargs): return self.__wrapped__.__exit__(*args, **kwargs) __iter__ = make_proxy_method(iter) def __call__(self, *args, **kwargs): return self.__wrapped__(*args, **kwargs) def __reduce__(self): return identity, (self.__wrapped__,) def __reduce_ex__(self, protocol): return identity, (self.__wrapped__,)