Package: class_proxy
Introductionclass_proxycan be used to create simple proxy objects for any value. The
proxy can be any class.For example, lets say that we have a parser and need to be able to tell where
a value came from (source file name, line number and column). We might have aSourceclass handling this information:classSource(object):def__init__(self,filename,line,column):self.filename=filenameself.line=lineself.column=columndef__str__(self):return"file{}, line{}, column{}".format(self.filename,self.line,self.column)def__repr__(self):return"Source({!r},{!r},{!r})".format(self.filename,self.line,self.column)If we attach this source to some class we control, there is no problem. But
what if we want to tell where an integer, string or any scalar, builtin came
from?The solution is to make a wrapper for the scalar classes:importclass_proxyclassSourceValue(object):def__init__(self,source):self.source=sourcedef__str__(self):return"{}(from{})".format(class_proxy.instance(self),self.source)def__repr__(self):return"SourceValue({!r},{!r})".format(class_proxy.instance(self),self.source)IntProxy=class_proxy.wrap_with(int,SourceValue)StrProxy=class_proxy.wrap_with(str,SourceValue)number=IntProxy(123,Source('example',1,2))print(number)# will print: 1 (from file example, line 1, column 2)string=StrProxy('hello',Source('example',5,7))print(repr(number))# will print: hello (from file example, line 5, column 7)other_number=IntProxy(456,Source('example',7,8))print(number<other_number)# will print: TrueTheclass_proxy.instancecall will return the original, wrapped
instance for the proxy.Theclass_proxy.wrap_withcall will create a proxy class expecting the
wrapped value as the first parameter, while the remaining parameters are passed
to theSourceValueconstructor.When retrieving attributes from the proxy class (e.g.IntProxy), it
will look up the proxy base class first (e.g.SourceValue) and later
the wrapped class (e.g.int). This is the reason we care about what
class we’re wrapping: we want to be able to wrap the class too, not just
the instance.In case we’re looking up members from the instance, at first we will look it up
from the instance of the proxy class, and then from the wrapped class.If you don’t care about class values, you can also omit the wrapped class:GenericProxy=class_proxy.wrap_with(SourceValue)generic=GenericProxy(SomeGenericClass(1,2),Source('example',10,0))In this case, we wrap theobject.There is also a convenience wrapper for thewrap_withfunction, in case
you don’t need to define it for multiple wrapped classes:importclass_proxy@class_proxy.proxy_of(int)classIntProxy(object):defclamp(self,minval,maxval):ifself<minval:result=minvalelifself>maxval:result=maxvalelse:result=class_proxy.instance(self)returnIntProxy(result)value=IntProxy(50).clamp(-10,10)print(value)# will print: 10This also has a generic variant:importclass_proxy@class_proxy.proxyclassProxy(object):defmap(self,func):returnfunc(class_proxy.instance(self))value=Proxy(100)print(value.map(lambdaval:-val))# will print: -100Inspirationclass_proxywas heavily inspired byzyga/padme, an excellent tool for
creating proxy classes. However,padmehas a few problems:It generates a lot of logs, which can not only be annoying, but create odd
infinite recursions when trying to format the wrapped value for a log string.The proxied special methods are written out manually, which seems like an
unneccessary thing.So with these limitations I felt the need to write a similar tool that
addresses the issues above.History1.1.0 (2019-03-18)Custom names can be specified for bound class proxies (by default,
it is in the format of <proxy class>[<wrapped class>]).1.0.1 (2019-03-10)Added caching for created proxies.1.0.0 (2019-03-09)Inspired byzyga/padme.Written the basic code.Added files to make it more ‘project-y’.Committed togithub.com.