def test_instance_from_environment(self): os.environ.update(dict(LASTFM_API_KEY="a")) config = Config.instance() expected = { "api_key": "a", "api_secret": None, "username": None, "password": None, "session": None, } self.assertDictEqual(expected, config.to_dict()) os.environ.update( dict( LASTFM_API_KEY="a", LASTFM_API_SECRET="b", LASTFM_USERNAME="******", LASTFM_PASSWORD="******", LASTFM_SESSION="e", )) new_config = Config.instance() self.assertEqual(config, new_config) Config._instance = None expected = { "api_key": "a", "api_secret": "b", "username": "******", "password": md5("d"), "session": "e", } self.assertDictEqual(expected, Config.instance().to_dict())
def prepare_params(cls, params: Dict, sign: bool, stateful: bool, authenticate: bool) -> dict: """ Perform common parameter tasks before sending the web request. * Filter out None values, * Set the preferred api format ``json`` * Add the api key, session or signature based on the state flags :param Dict params: A dictionary of body or query string params :param bool sign: Sign the request with the api secret :param bool stateful: Add the session key to the params :param bool authenticate: Add the username and auth token to the params :rtype: Dict """ cfg = Config.instance() params = { k: str(int(v is True) if isinstance(v, bool) else v) for k, v in params.items() if v is not None } params.update({"format": "json", "api_key": cfg.api_key}) if authenticate: params.update({ "username": cfg.username, "authToken": cfg.auth_token }) if stateful: params.update({"sk": cls.get_session().key}) if authenticate or stateful or sign: params.update({"api_sig": cls.sign(params)}) return params
def prepare_params(cls, params: Dict, sign: bool, stateful: bool, authenticate: bool) -> dict: """ Perform common parameter tasks before sending the web request. * Filter out None values, * Set the preferred api format ``json`` * Add the api key, session or signature based on the state flags :param Dict params: A dictionary of body or query string params :param bool sign: Sign the request with the api secret :param bool stateful: Add the session key to the params :param bool authenticate: Add the username and auth token to the params :rtype: Dict """ cfg = Config.instance() params = dict((k, str(int(v is True) if type(v) == bool else v)) for k, v in params.items() if v is not None) params.update(dict(format="json", api_key=cfg.api_key)) if authenticate: params.update(dict(username=cfg.username, authToken=cfg.auth_token)) if stateful: params.update(dict(sk=cls.get_session().key)) if authenticate or stateful or sign: params.update(dict(api_sig=cls.sign(params))) return params
def setUp(self): super(ConfigTests, self).setUp() self.config = Config.instance() Config._instance = None for k in self.keys: try: del os.environ["LASTFM_{}".format(k.upper())] except KeyError: pass
def setUp(self): super().setUp() self.config = Config.instance() Config._instance = None for k in self.keys: try: del os.environ[f"LASTFM_{k.upper()}"] except KeyError: pass
def get_session(cls) -> "AuthSession": # type: ignore """ Return the session from configuration or attempt to authenticate the configuration user. :rtype: :class:`~pydrag.models.auth.AuthSession` """ cfg = Config.instance() if not cfg.session: from pydrag.models.auth import AuthSession cfg.session = AuthSession.authenticate() return cfg.session
def test_instance_from_arguments(self): config = Config.instance("key") expected = { "api_key": "key", "api_secret": None, "password": None, "session": None, "username": None, } self.assertDictEqual(expected, config.to_dict()) self.assertEqual(config, Config.instance()) new_config = Config.instance("a", "b", "c", "d", "e") expected = { "api_key": "a", "api_secret": "b", "username": "******", "password": md5("d"), "session": "e", } self.assertDictEqual(expected, new_config.to_dict()) self.assertEqual(new_config, Config._instance) self.assertNotEqual(config, Config._instance)
def sign(params: Dict) -> str: """ Last.fm signing formula for webservice calls. Exclude format, sort params, append the api secret key and hash the params string. :param Dict params: :rtype: str """ keys = sorted(params.keys()) keys.remove("format") signature = [str(k) + str(params[k]) for k in keys if params.get(k)] signature.append(str(Config.instance().api_secret)) return utils.md5("".join(signature)) # type: ignore
def _perform( cls, method: str, bind: Type[BaseModel], flatten: Optional[str], params: Dict, sign: bool, stateful: bool, authenticate: bool, ): """ Orchestrate the request, error handling and response deserialization. :param str method: Http method POST/GET :param bind: Class type to construct from the api response. :type bind: :class:`~pydrag.models.common.BaseModel` :param str flatten: A dot separated string used to flatten nested :param Dict params: A dictionary of body or query string params :param bool sign: Sign the request with the api secret :param bool stateful: Requires a session :param bool authenticate: Perform an authentication request :rtype: :class:`~pydrag.models.common.BaseModel` """ data: Dict = {} query: Dict = {} if method == "GET": query = cls.prepare_params(params, sign, stateful, authenticate) else: data = cls.prepare_params(params, sign, stateful, authenticate) cfg = Config.instance() response = request(method=method, url=cfg.api_url, data=data, params=query) response.raise_for_status() body = response.json(object_pairs_hook=pythonic_variables) cls.raise_for_error(body) obj = cls.bind_data(bind, body, flatten) obj.params = params return obj
def test_instance_raises_exception(self): with self.assertRaises(AssertionError) as cm: Config.instance() self.assertEqual("Provide a valid last.fm api key.", str(cm.exception))
def auth_url(self): return Config.auth_url.format(self.token, Config.instance().api_key)
def setUp(self): self.maxDiff = None Config.instance().session = None super(MethodTestCase, self).setUp()
import json import os import re from unittest import TestCase import vcr from pydrag.models.common import Config try: config = Config.instance() except AssertionError: Config.instance(api_key="key") where_am_i = os.path.dirname(os.path.realpath(__file__)) fixtures_dir = os.path.join(where_am_i, "models", "fixtures") censored_parameters = [ ("token", "USER_TOKEN"), ("sk", "USER_SESSION"), ("api_sig", "API_SIG"), ("api_key", "LAST_FM_API_KEY"), ("authToken", "USER_AUTH_TOKEN"), ] def censor_response(response): body = response["body"]["string"] body = re.sub(b"[A-Za-z0-9-_]{32}", b"CENSORED", body) response["body"]["string"] = body return response