Mike
Mike

Reputation: 4259

Python client authentication with decorators

I want to build a python client on top of a REST API that uses authentication with a api_token. Hence all api calls require the api_token. As it is pretty ugly to add a field

'token=...'

e.g.

a = f1(5, token='token')
b = f2(6, 12, token='token')
c = f3(2, 'a', token='token')

where internally f1 and f2 delegate to the REST api

to each function call. What I would like to have is something like:

auth = authenticate('token')

a = f1(5)
b = f2(6, 12,)
c = f3(2, 'a')

What I can do is to create a class and make all functions member functions. Hence, we would have:

auth = calculator('token')
a = auth.f1(5)
b = auth.f2(6, 12,)
c = auth.f3(2, 'a')

but that would also be somewhat ugly. I am trying to get this to work with decorators, but to no avail so far.

class authenticate:
   def __init__(self, token):
       self.token = token

   def __call__(self, func):
       def functor(*args, **kwargs):
           return func(*args, **kwargs, key=self.authentication)
       return functor 

@authenticate
def f1(a, key):
    data = a
    result = requests.get(1, data, key)
    return result

However, this seems to be going nowhere. I am also wondering whether this might work at all as decorators are executed at import time and the token is added at runtime.

Any suggestions on how to make this work or anyone know if there is another standard pattern for this?

Upvotes: 0

Views: 2937

Answers (1)

Mike
Mike

Reputation: 4259

So after some hacking around we came up with the following:

class authenticate:
   # start empty key
   key = None

   @classmethod
   """ add the token """
   def set_key(cls, token):
       cls.token = token

   def __init__(self, func=None):
       if func is not None:
           self.func = func
       else:
           print('no function')

   def __call__(self, *arg):
       """
       add authentication to function func
       """
       ret = self.func(*arg, auth_key=self.key)
       return ret

@authenticate
def f1(a, key):
    data = a
    result = requests.get(1, data, key)
    return result

Then you can run code like:

authentication_key = 'token'

print('Initiate class')
authenticate().set_key(key=authentication_key)

print('Run f1(5)')
a1 = f1(5) # no token needed!
a2 = f2(6, 12) # again no token needed as it is in the decorator
print(a1)

This works more or less as I hoped and I find it cleaner than the class methods. If anyone has a better suggestion or improvements let me know.

Upvotes: 1

Related Questions