aop in python api design

Post on 16-May-2015

1.855 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

Talk at PyGrunn 2012 by Douwe van der Meij.

TRANSCRIPT

AOP in Python API design

Douwe van der Meij

Goldmund, Wyldebeast & Wunderliebe

Outline

● Problem description ● Aspect Oriented Programming ● Implementation

Case

● BioBench:○ Application for biogas installations

● Enter measurements ● Normalize data ● Calculate plant performance

Problem description

● Large code-base ● A lot of calculations ● Django web-interface ● Make calculations available via an API

Problem description

● Is there a library/framework? ● Are the alternatives? ● What is the actual impact of the feature? ● What aspects to take into account?

Problem description

● Aspects:○ Security○ Statistics / Logging○ Serialization○ More?

Problem description

def add(lhs, rhs): return lhs + rhs

Problem description

def add(lhs, rhs): return lhs + rhs def api_add(request): return add(request.args['lhs'], request.args['rhs'])

Problem description

def add(lhs, rhs): return lhs + rhs def api_add(request):

if request.args['token'] == valid: return add(request.args['lhs'], request.args['rhs']) raise Exception('Security error')

Problem description

def add(lhs, rhs): return lhs + rhs def api_add(request):

if request.args['token'] == valid: database.query().alter usage counter return add(request.args['lhs'], request.args['rhs']) raise Exception('Security error')

Problem description

def add(lhs, rhs): return lhs + rhs def api_add(request):

if request.args['token'] == valid: database.query().alter usage counter result = add(request.args['lhs'], request.args['rhs']) return '<xml>{0}</xml>'.format(result) raise Exception('Security error')

Problem description

def add(lhs, rhs): return lhs + rhs def api_add(request):

if request.args['token'] == valid: database.query().alter usage counter result = add(request.args['lhs'], request.args['rhs']) return '<xml>{0}</xml>'.format(result) raise Exception('Security error')

Problem description

def add(lhs, rhs): return lhs + rhs def api_add(request):

if request.args['token'] == valid: database.query().alter usage counter result = add(request.args['lhs'], request.args['rhs']) return '<xml>{0}</xml>'.format(result) raise Exception('Security error')

Problem description

Code base

Problem description

Code base

● Security

Problem description

Code base

● Statistics / Logging

Problem description

Code base

● Serialization

Problem description

Code base

● Dispatcher

Problem description

Code base

● Scattered / tangled code

Aspect Oriented Programming

● What is AOP? ● Separation of concerns (aspects) ● Avoid scattering / tangling

Code base

● Scattered / tangled code

Aspect Oriented Programming

Code base

● Avoid scattering

Aspect Oriented Programming

Code base

● Avoid tangling

Aspect Oriented Programming

Aspect Oriented Programming

● How to implement these marvelous concepts?○ In pure python please!

Aspect Oriented Programming

● Decorators!

Aspect Oriented Programming

● Aspect:○ Pointcuts○ Join points○ Advices

■ Before advices■ After advices■ Around advices

Aspect Oriented Programming

● Before advice○ Must execute the function (no side-effects)

def aspect(function): def advice(*args, **kwargs):

do something here return function(*args, **kwargs) return advice

Aspect Oriented Programming

● After advice○ Must execute the function (no side-effects)

def aspect(function): def advice(*args, **kwargs): result = function(*args, **kwargs)

do something here return result return advice

Aspect Oriented Programming

● Around advice○ Allowed to bypass the function

def aspect(function): def advice(*args, **kwargs):

do something here result = function(*args, **kwargs)

do something here return result return advice

Implementation

● How to apply it to our case?

Implementation

● The decorators

Implementation

● Security aspect○ Around advice

def secure(function): def advice(*args, **kwargs):

if valid token in request object: return function(*args, **kwargs) raise Exception('No valid token provided') return advice

Implementation

● Statistics aspect○ Before advice

def statistics(function): def advice(*args, **kwargs):

increase API usage count for the user logged in return function(*args, **kwargs) return advice

Implementation

● Serialization aspect○ Around advice

def serialize(function): def advice(format, *args, **kwargs): if not format in ['html', 'xml', 'json']:

raise exception result = function(*args, **kwargs)

make a http response of 'result' in the right format return advice

Implementation

● Dispatcher aspect○ Around advice

def dispatch(function): def advice(*args, **kwargs):

proxy the API call to a call to the core system return advice

Implementation

mapping = { 'api_function': (core_function, ['lhs', 'rhs']), 'create_token': (create_token, ['username', 'password']),}

Implementation

def dispatch(function): def advice(*args, **kwargs):

if API call in proxy mapping: core_function, params = mapping[API call] kwargs.update(extract(params, request)) return function(proxy=core_function,

params=params, *args, **kwargs)

raise exception return advice

Implementation

● The API itself

Implementation

@secure@serialize@statistics@dispatchdef api_call(*args, **kwargs): proxy_function = kwargs['proxy'] params = kwargs['params']

return proxy_function(extract params from kwargs)

Conclusion

● AOP offers some brilliant concepts in software engineering

● Separate your concerns / aspects

○ Avoid classical scattering and tangling

Questions?

● Thank you!

vandermeij@gw20e.com@douwevandermeij

top related