Created
January 23, 2017 20:58
-
-
Save typemytype/0554f1e313d7d94455e09a347e26892e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Implementation(object): | |
| """ | |
| Implementation object collection all methods for a given implementation | |
| """ | |
| def __init__(self): | |
| self.funcMap = {} | |
| self._currentInstance = None | |
| def __get__(self, instance, instanceCls): | |
| # set the current instance | |
| self._currentInstance = instance | |
| # return self, this mains the object will __getattr__ calls for methods | |
| return self | |
| def __getattr__(self, attr): | |
| # get a method | |
| # if the curren instance does not have the attr | |
| # raise an not implemented error | |
| if not hasattr(self._currentInstance, attr): | |
| raise NotImplementedError("'%s' not implemented" % attr) | |
| def attrWrapper(*args, **kwargs): | |
| # loop over all parent classes | |
| for cls in self._currentInstance.__class__.__mro__: | |
| # check if the cls has that attr | |
| if hasattr(cls, attr): | |
| # get the function | |
| func = getattr(cls, attr) | |
| # check if the function is wrapped | |
| if hasattr(func, "wrappedFunc"): | |
| # check the wrapped function is in the function map | |
| # it can happen multiple implementation have wrapped functions | |
| # with the same attr name | |
| if func.wrappedFunc == self.funcMap[attr]: | |
| # call the function with the current instance | |
| result = func.wrappedFunc(self._currentInstance, *args, **kwargs) | |
| # reset the currentInstance | |
| self._currentInstance = None | |
| # return the result | |
| return result | |
| # decorate by returning an attrWrapper | |
| return attrWrapper | |
| def implement(self, func): | |
| # `implement` decorates the function | |
| # accisible through an implementation | |
| # if this is called without an implementation | |
| # it will search in the parent classes for the same | |
| # function by name, if not available it will raise | |
| # a NotImplementedError | |
| # get the name of the decorated func | |
| name = func.__name__ | |
| # map the func | |
| self.funcMap[name] = func | |
| # the decorator wrapper | |
| def wrapper(instance, *args, **kwargs): | |
| found = False | |
| # loop over all parent classes | |
| for cls in instance.__class__.__mro__: | |
| # check if func name is available | |
| if hasattr(cls, name): | |
| # get the func | |
| func = getattr(cls, name) | |
| # check if the function is not a wrapped func | |
| if not hasattr(func, "wrappedFunc"): | |
| # set found flag to true | |
| found = True | |
| break | |
| # if not found raise NotImplementedError | |
| if found is False: | |
| raise NotImplementedError("'%s' not implemented" % name) | |
| # call that found function | |
| return func(instance, *args, **kwargs) | |
| # tag the wrapped decorator with the wrapped func | |
| wrapper.wrappedFunc = func | |
| # decorate by returning an wrapper | |
| return wrapper | |
| #### | |
| class BaseContour(object): | |
| def aMethod(self, point): | |
| return "BaseContour aMethod", point | |
| class BetaContour(BaseContour): | |
| beta = Implementation() | |
| @beta.implement | |
| def aMethod(self, point): | |
| return "BetaContour aMethod", point | |
| @beta.implement | |
| def moreMethods(self, foo): | |
| return "BetaContour moreMethods", foo | |
| class Contour(BetaContour): | |
| roboFont = Implementation() | |
| @roboFont.implement | |
| def aMethod(self, point, test): | |
| return "Contour aMethod", point, test | |
| @BetaContour.beta.implement | |
| def testing(self, someThing): | |
| return "Contour testing", someThing | |
| c = Contour() | |
| print c.aMethod((10, 10)) | |
| print c.beta.aMethod((20, 20)), "-" | |
| print c.roboFont.aMethod((30, 30), False) | |
| print c.roboFont.aMethod("ll", "kk") | |
| print c.beta.moreMethods("bar") | |
| # print c.roboFont.aMethoddfsdf("ll", "kk") # raise error | |
| print c.beta.testing("hello") | |
| # print c.testing("hello") # raise error |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment