Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python3 async version #5

Open
pinhopro opened this issue Jun 5, 2018 · 2 comments
Open

python3 async version #5

pinhopro opened this issue Jun 5, 2018 · 2 comments

Comments

@pinhopro
Copy link

pinhopro commented Jun 5, 2018

Hi guys,

I implemented a python3 async version of emailage api client.

here is the code

#!/usr/bin/env python3
from uuid import uuid4
import time
import json
import re
from six import b


import urllib
from hashlib import sha1
import hmac
import base64

import aiohttp


class AioEmailageClient(object):
    def __init__(self, secret, token):
        self.secret, self.token = secret, token
        self.hmac_key = token + '&'
        self.domain = 'https://api.emailage.com'
        self.session = aiohttp.ClientSession()

    async def request(self, endpoint, **params):
        url = self.domain + '/emailagevalidator' + endpoint + '/'
        params = dict(
            format='json',
            oauth_consumer_key=self.secret,
            oauth_nonce=str(uuid4()),
            oauth_signature_method='HMAC-SHA1',
            oauth_timestamp=int(time.time()),
            oauth_version="1.0",
            **params
        )

        def _quote(obj):
            return urllib.parse.quote(str(obj), safe='')

        def _normalize_query_parameters(params):
            return '&'.join(map(lambda pair: '='.join([_quote(pair[0]), _quote(pair[1])]), sorted(params.items())))

        def _concatenate_request_elements(method, url, query):
            return '&'.join(map(_quote, [str(method).upper(), url, query]))

        def _hmac_sha1(base_string, hmac_key):
            """9.2.  HMAC-SHA1"""
            hash = hmac.new(b(hmac_key), b(base_string), sha1)
            return hash.digest()

        def _encode(digest):
            """9.2.1.  Generating Signature"""
            return base64.b64encode(digest).decode('ascii').rstrip('\n')

        query = _normalize_query_parameters(params)
        base_string = _concatenate_request_elements('GET', url, query)
        digest = _hmac_sha1(base_string, self.hmac_key)
        params['oauth_signature'] = _encode(digest)

        async with self.session.get(url,params=params) as resp:
            # Remove any unreadable characters from response payload
            json_data = re.sub(r'^[^{]+', '', await resp.text())
            return json.loads(json_data)

    async def query(self, query, **params):
        if type(query) is tuple:
            query = '+'.join(query)
        params['query'] = query
        return await self.request('', **params)

    async def query_email(self, email, **params):
        if not re.match(r'^[^@\s]+@([^@\s]+\.)+[^@\s]+$', email):
            raise ValueError('{} is not a valid email address.'.format(email))
        return await self.query(email, **params)
@gtraines
Copy link

gtraines commented Jun 6, 2018

Awesome, how is it performing for you compared to the synchronous version?

@pinhopro
Copy link
Author

pinhopro commented Jun 6, 2018

I didn't make any performance tests yet. The reason I implemented was because I didn't want to use a blocking library in my app, which is fully async.

Anyway, I hope this code snippet helps you to develop an async version of the library.

Thanks,
Rodrigo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants