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


● Security
      Code base
Problem description


● Statistics / Logging
       Code base
Problem description


● Serialization
       Code base
Problem description


● Dispatcher
      Code base
Problem description


● Scattered / tangled code
      Code base
Aspect Oriented Programming


● What is AOP?

● Separation of concerns (aspects)

● Avoid scattering / tangling
Aspect Oriented Programming


● Scattered / tangled code
      Code base
Aspect Oriented Programming


● Avoid scattering
      Code base
Aspect Oriented Programming


● Avoid tangling
      Code base
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   proxy mapping:
                      in
            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
@dispatch
def 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

AOP in Python API design