Package: metautils3
Experimental utilities for writing and composing metaclasses.For safe and stable metaclass utilities, seemetautilsTemplate ModelWhy do we need or want to write class templates.Consider the two metaclasses.classAllLower(type):def__new__(mcls,name,bases,dict_):dict_={k.lower():vfork,vindict_.items()}returnsuper().__new__(mcls,name,bases,dict_)classMethodCatcher(type):def__new__(mcls,name,bases,dict_):dict_['methods']=[vforvindict_.values()ifcallable(v)]returnsuper().__new__(mcls,name,bases,dict_)What would we do if we wanted to make a class that usedBOTHof these
metaclasses? Using a class that subclasses bothAllLowerandMethodCatcherdoes not work, what we want is a way to chain them.With the class template model, we could have written our metaclasses
as:frommetautils3importT,templatedclassAllLower(T):@templateddef__new__(mcls,name,bases,dict_):dict_={k.lower():vfork,vindict_.items()}returnsuper().__new__(mcls,name,bases,dict_)classMethodCatcher(T):@templateddef__new__(mcls,name,bases,dict_):dict_['methods']=[vforvindict_.values()ifcallable(v)];returnsuper().__new__(mcls,name,bases,dict_)Python 2 style super calls will also work, like:super(AllLower, mcls).
We can write the above classes without using super by delagating toTjust like we would delegate to a concrete class, for example:frommetautils3importT,templatedclassAllLower(T):@templateddef__new__(mcls,name,bases,dict_):dict_={k.lower():vfork,vindict_.items()}returnT.__new__(mcls,name,bases,dict_)classMethodCatcher(T):@templateddef__new__(mcls,name,bases,dict_):dict_['methods']=[vforvindict_.values()ifcallable(v)];returnT.__new__(mcls,name,bases,dict_)Inside the context of a templated function,Twith refer to the concrete
class that was used to instantiate an instance of the template.
Another name that will be changed out from under you is the class name
itself. When you are in the context of a method, the class name will actually
resolve to the concrete type.Now we can define classes that useBOTHof these metaclasses like so:classC(object,metaclass=MethodCatcher(AllLower())):defF():passdefg():passa='a'B='b'We can see that this applied the composition of the metaclasses.>>>C.f<function__main__.C.F>>>>C.g<function__main__.C.g>>>>C.b'b'>>>C.a'a'>>>C.methods[<function__main__.C.g>,<function__main__.C.F>]The order that the metaclasses are composed is explicit as they act as
transformers over each other.TemplateWhile the previous example only showed metaclasses, you can use this for any
class; however, it is most useful for metaclasses where having a compatible
metaclass hierarchy is important.ATemplateis a callable that takes atypeobject and
returns a newtypeobject. It takes the following arguments:base: A type object.default:type.adjust_name: Should we prepend the name of thebaseto the
new type object.default:True.These can be chained together with any concrete metaclass at the end,
e.g.:new_class=m(n,p(q(...z(type)...)))You can also use the compose function to do this:frommetautils3importcomposenew_class_template=compose(m,n,p,q,...,z)Differences withmetautilsmetautils3 uses far more experimental features, including bytecode and code
object transformations that allow for more work to be done implicitly. This is
how theTobject can refernece the template argument inside of a method, or
how we can getsuperto work as intended. This package also calls intoctypesand other CPython specific code, making it less portable and more
difficult to maintain. This is mainly an interesting proof of concept to pushmetautilsto the limits. For any production code, I have to recommend you
use the more stable version.