Last active
May 19, 2020 08:45
-
-
Save probablytom/022c138e1487175f893a5899e9026f61 to your computer and use it in GitHub Desktop.
A Python3 compatible Asp implementation
This file contains 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 AspectHooks: | |
pre_rules = list() | |
around_rules = list() | |
post_rules = list() | |
def __enter__(self, *args, **kwargs): | |
self.old_import = __import__ | |
import builtins | |
builtins.__import__ = self.__import__ | |
def __exit__(self, *args, **kwargs): | |
builtins.__import__ = self.old_import | |
def __import__(self, *args, **kwargs): | |
mod = self.old_import(*args, **kwargs) | |
def build_wrapper(target): | |
@wraps(target) | |
def wrapper(*args, **kwargs): | |
pre, around, post = self.get_rules(target.__name__) | |
for advice in pre: | |
advice(target, *args, **kwargs) | |
ret = target(*args, **kwargs) ## TODO: implement around | |
for advice in post: | |
post_return = advice(target, ret, *args, **kwargs) | |
ret = ret if post_return is None else post_return | |
return ret | |
return wrapper | |
def apply_hooks(target_object): | |
for item_name in filter(lambda p: isinstance(p, str) and len(p) > 1 and p[:2] != "__", dir(target_object)): | |
item = getattr(target_object, item_name) | |
if isfunction(item) or ismethod(item): | |
setattr(target_object, item_name, build_wrapper(item)) | |
elif isclass(item): | |
apply_hooks(item) | |
apply_hooks(mod) | |
return mod | |
@classmethod | |
def add_prelude(cls, rule, advice, urgency=0): | |
AspectHooks.pre_rules.append((re.compile(rule), advice, urgency)) | |
@classmethod | |
def add_around(cls, rule, advice, urgency=0): | |
AspectHooks.around_rules.append((re.compile(rule), advice, urgency)) | |
@classmethod | |
def add_encore(cls, rule, advice, urgency=0): | |
AspectHooks.post_rules.append((re.compile(rule), advice, urgency)) | |
def get_rules(self, methodname): | |
pre, around, post = list(), list(), list() | |
for pattern, advice, urgency in AspectHooks.pre_rules: | |
if pattern.match(methodname) is not None: | |
pre.append((advice, urgency)) | |
for pattern, advice, urgency in AspectHooks.around_rules: | |
if pattern.match(methodname) is not None: | |
around.append((advice, urgency)) | |
for pattern, advice, urgency in AspectHooks.post_rules: | |
if pattern.match(methodname) is not None: | |
post.append((advice, urgency)) | |
urgencykey = lambda ad_urg_tuple: -ad_urg_tuple[1] # Negative so we get the highest urgency first | |
pre, around, post = sorted(pre, key=urgencykey), sorted(around, key=urgencykey), sorted(post, key=urgencykey) | |
advice_functions = list(map(lambda x: x[0], pre)), list(map(lambda x: x[0], around)), list(map(lambda x: x[0], post)) | |
return advice_functions | |
if __name__ == "__main__": | |
# Activate aspects on testmod | |
with AspectHooks(): | |
import testmod | |
# Make an encore and apply it to functions which match any name | |
invocations = 0 | |
def encore(*args, **kwargs): | |
global invocations | |
invocations += 1 | |
AspectHooks.add_encore(".*", encore) | |
# Quick assertions to make sure everything's kosher | |
assert testmod.dosomething() is None | |
assert testmod.double(5) is 10 | |
assert testmod.Calc().triple(4) is 12 | |
assert invocations == 3 |
This file contains 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
def double(n): | |
return n*2 | |
class Calc: | |
def triple(self, n): | |
return n * 3 | |
def dosomething(): | |
pass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment