Source code for swdata.planets

"""
These are planet data providers.

ATM there is only 60 planets in the pool so this is not a problem to keep
them in memory. When circumstances change, and the pool of planets would
increase significantly, we can implement a more efficient data provider that
would build index an index only once store it only update missing planets
when they are requested.
"""

import operator
from abc import ABC, abstractmethod
from dataclasses import dataclass
from threading import RLock

import backoff
import cachetools
import petl as etl
import requests
from cached_property import cached_property

from swdata.adapters import results_generator
from swdata.settings import SWAPI_URL


[docs]class PlanetProvider(ABC):
[docs] @abstractmethod def get_name(self, url: str): ...
[docs]class LazyPlanetProvider(PlanetProvider): """ Lazy and slow planet provider usable only when the number queries planets is small in comparison to the total number of available planets. """ CACHE_TIME = 60 * 60 CACHED_PLANETS = 100 def __init__(self) -> None: self._cache = cachetools.TTLCache( maxsize=self.CACHED_PLANETS, ttl=self.CACHE_TIME, ) self._rlock = RLock()
[docs] @cachetools.cachedmethod( cache=operator.attrgetter('_cache'), lock=operator.attrgetter('_rlock'), ) def get_name(self, url): response = self._get(url) name = response.json()['name'] return name
@backoff.on_exception(backoff.expo, Exception, max_tries=3) def _get(self, url): response = requests.get(url) response.raise_for_status() return response
[docs]class EagerPlanetProvider(PlanetProvider): """ Eager provider loading all planets into memory upfront. """
[docs] def get_name(self, url): return self.planets[url]
@cached_property def planets(self): data = results_generator(f'{SWAPI_URL}planets/') return etl.fromdicts(data, header=('url', 'name')).skip(1).dict()