Skip to content

Instantly share code, notes, and snippets.

@thulasi-ram
Created May 2, 2017 20:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thulasi-ram/38c073646321f07592abee0e621f9f26 to your computer and use it in GitHub Desktop.
Save thulasi-ram/38c073646321f07592abee0e621f9f26 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
import re
"""
Exemplo didático de implementação de programação orientada
a aspectos com Python.
Sem otimizações que seriam prematuras
Caso deseje um uso real programação orientada a aspectos com python,
existem alguns projetos bem mantidos em repositórios apropriados
na internet. È melhor usar um desses do que re-inventar a roda
"""
class Aspecter(type):
"""
Meta classe usada por classes que terão métodos orientados
a aspecto (com os join-points e cross-cut points e etc.
O objeto aspect_rules contém todas as regras de aspecto
"""
aspect_rules = []
wrapped_methods = []
def __new__(cls, name, bases, dict):
"""
Inicialização de classe que contém métodos orientados a aspectos.
Basicamente anota todos os métodos da classe de forma que a cada
chamada possa ser verificado se há uma regra correspondente
aos mesmos:
"""
for key, value in dict.items():
if hasattr(value, "__call__") and key != "__metaclass__":
dict[key] = Aspecter.wrap_method(value)
return type.__new__(cls, name, bases, dict)
@classmethod
def register(cls, name_pattern="", in_objects=(), out_objects=(),
pre_function=None,
post_function=None):
"""
Método usado para registrar uma nova regra de aspecto.
O registro pode ser feito dinamicamente em tempo de execução
name_pattern: é uma expressão regular que casa com o nome dos
métodos. em branco, casa com todos os métodos.
Em particular, note que esse esquema simplificado não dá conta
de chamar uma pre_function baseando-se em out_objects
"""
# Método to simples que poderia ser usado um append direto em
# "aspect rules"
rule = {"name_pattern": name_pattern, "in_objects": in_objects,
"out_objects": out_objects,
"pre": pre_function, "post": post_function}
cls.aspect_rules.append(rule)
@classmethod
def wrap_method(cls, method):
def call(*args, **kw):
pre_functions = cls.matching_pre_functions(method, args, kw)
for function in pre_functions:
function(*args, **kw)
results = method(*args, **kw)
post_functions = cls.matching_post_functions(method, results)
for function in post_functions:
function(results, *args, **kw)
return results
return call
@classmethod
def matching_names(cls, method):
return [rule for rule in cls.aspect_rules
if re.match(rule["name_pattern"], method.func_name)
or rule["name_pattern"] == ""
]
@classmethod
def matching_pre_functions(cls, method, args, kw):
all_args = args + tuple(kw.values())
return [rule["pre"] for rule in cls.matching_names(method)
if rule["pre"] and
(rule["in_objects"] == () or
any((type(arg) in rule["in_objects"] for arg in all_args)))
]
@classmethod
def matching_post_functions(cls, method, results):
if type(results) != tuple:
results = (results,)
return [rule["post"] for rule in cls.matching_names(method)
if rule["post"] and
(rule["out_objects"] == () or
any((type(result) in rule["out_objects"] for result in results)))
]
if __name__ == "__main__":
#testing
class Address(object):
def __repr__(self):
return "Address..."
class Person(object):
__metaclass__ = Aspecter
def updateAddress(self, address):
pass
def __str__(self):
return "person object"
def log_update(*args, **kw):
print "Updating object %s" %str(args[0])
def log_address(*args, **kw):
addresses = [arg for arg in (args + tuple(kw.values()))
if type(arg) == Address]
print addresses
Aspecter.register(name_pattern="^update.*", pre_function=log_update)
Aspecter.register(in_objects=(Address,), pre_function=log_address)
p = Person()
p.updateAddress(Address())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment