Last active
June 15, 2016 02:34
-
-
Save justanr/d045460e318298c6f7f16534b46d9451 to your computer and use it in GitHub Desktop.
Proof of Concept for adding kinda Implicits to Python
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 Thing: | |
def r(self): | |
pass | |
class HasWhatever(metaclass=ImplicitMeta, companion=Thing): | |
whatever = lambda s: print(s, 1) | |
@property | |
def nine(self): | |
return 'nine' | |
@implicit(Thing) | |
def name(inst): | |
print("Getting name: ", inst.__class__.__name__) | |
class T: | |
a = 5 | |
def wut(*args): | |
print("implicit wut", args) | |
@classmethod | |
def w(cls): | |
print(cls) | |
@implicit(Thing, name='huh') | |
@staticmethod | |
def huh(): | |
print('huh?') | |
implicit(Thing)(T()) | |
inst = Thing() | |
inst.wut() | |
inst.name() | |
inst.whatever() | |
print(inst.a) | |
print(inst.nine) | |
inst.w() | |
inst.huh() |
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
from types import FunctionType | |
from functools import wraps, update_wrapper | |
def maybe_static(f, inst): | |
@wraps(f) | |
def tryer(*a, **k): | |
try: | |
return f(inst, *a, **k) | |
except TypeError: | |
return f(*a, **k) | |
return tryer | |
class GetAttr: | |
""" | |
Replaces the normal __getattr__ on a class with a descriptor that looks | |
through a series of registered implicits and attempts to pull the desired | |
method off of them or re-raise the AttributeError. | |
""" | |
def __init__(self, getattr): | |
self._getattr = getattr | |
self._look_at = [] | |
def add_implicit(self, type): | |
self._look_at.append(type) | |
def __call__(self, inst, attr): | |
try: | |
return self._getattr(inst, attr) | |
except AttributeError: | |
for implicit in self._look_at: | |
if hasattr(implicit, attr): | |
attr = getattr(implicit, attr) | |
if hasattr(attr, '__func__'): | |
return update_wrapper(lambda *a, **k: attr(*a, **k), attr) | |
if callable(attr): | |
return maybe_static(attr, inst) | |
elif hasattr(attr, '__get__'): | |
return attr.__get__(inst, type(inst)) | |
else: | |
return attr | |
else: | |
raise | |
def no_attr(inst, attr): | |
"Apparently __getattr__ doesn't exist until defined" | |
raise AttributeError("type object '{}' has no attribute '{}'".format(type(inst).__name__, attr)) | |
def getter(getter): | |
"""Transforms an instance of GetAttr into a function that can be bound to a class""" | |
@wraps(getter, assigned=['add_implicit']) | |
def g(inst, attr): | |
return getter(inst, attr) | |
return g | |
def make_implicit(companion): | |
""" | |
Helper function to transform the __getattr__ of a class | |
into a GetAttr descriptor. | |
""" | |
if not hasattr(companion, '__getattr__'): | |
companion.__getattr__ = getter(GetAttr(no_attr)) | |
elif not hasattr(companion.__getattr__, 'add_implicit'): | |
companion.__getattr__ = getter(GetAttr(companion.__getattr__)) | |
class ImplicitMeta(type): | |
""" | |
Automatically registers a class as an implicit to another class. | |
""" | |
def __new__(mcls, name, bases, attrs, companion): | |
cls = super().__new__(mcls, name, bases, attrs) | |
make_implicit(companion) | |
companion.__getattr__.add_implicit(cls) | |
return cls | |
def __init__(mcls, name, bases, attrs, companion): | |
super().__init__(name, bases, attrs) | |
class implicit: | |
""" | |
Decorator/callable to register a function, class or object as an implicit | |
on another class. | |
""" | |
_ANON_PATTERN = 'AnonImplicit[{}]' | |
def __init__(self, companion, name=None): | |
self._companion = companion | |
self._name = name | |
def __call__(self, implicit): | |
if isinstance(implicit, (staticmethod, classmethod, property, FunctionType)): | |
self._func(implicit) | |
elif isinstance(implicit, type): | |
self._type(implicit) | |
else: | |
self._inst(implicit) | |
return implicit | |
def _func(self, implicit): | |
name = self._name or implicit.__name__ | |
if name == '<lambda>': | |
raise ValueError("Provide name to constructor when providing lambda") | |
ImplicitMeta(self._ANON_PATTERN.format(name), (object,), {name: implicit}, self._companion) | |
def _type(self, implicit): | |
name = self._name or implicit.__name__ | |
ImplicitMeta(self._ANON_PATTERN.format(name), (implicit,), {}, self._companion) | |
def _inst(self, implicit): | |
make_implicit(self._companion) | |
self._companion.__getattr__.add_implicit(implicit) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment