В настоящее время у меня есть этот класс для выполнения запросов к API и кеширования ответа JSON:
import os
import pathlib
import json
import hashlib
import time
import requests
class NoJSONResponseError(Exception):
pass
class JSONRequestCacher(object):
"""Manage a JSON object through the cache.
Download the associated resource from the provided URL
when need be and retrieve the JSON from a cached file
if possible.
"""
def __init__(self, duration=86400, cachedir=None):
self.duration = duration
self.cachedir = self._get_cachedir(cachedir)
self._validate_cache()
def _get_cachedir(self, cachedir):
if cachedir is None:
cachedir = os.environ.get(
'CUSTOM_CACHEDIR',
pathlib.Path(pathlib.Path.home(), '.custom_cache/')
)
return cachedir
def _validate_cache(self):
"""Create the cache directory if it doesn't exist"""
self.cachedir.mkdir(parents=True, exist_ok=True)
def _request(self, url):
"""Perform the retrieval of the requested JSON data"""
return requests.get(url)
def save(self, raw, cachefile):
"""Save the provided raw JSON data into the cached file"""
with open(cachefile, 'w') as out:
json.dump(raw, out)
def load(self, cachefile):
"""Retrieve the saved JSON data from the cached file"""
with open(cachefile) as cached:
return json.load(cached)
def cache_is_valid(self, cachefile):
"""Check if cache exists and is more recent than the cutoff"""
if cachefile.is_file():
cache_age = time.time() - cachefile.stat().st_mtime
return cache_age < self.duration
return False
def request(self, url, refresh=False):
"""The JSON data associated to the given URL.
Either read from the cache or fetch from the web.
"""
urlhash = hashlib.md5(url.encode()).hexdigest()
cachefile = self.cachedir.joinpath(urlhash)
start = time.time()
if not refresh and self.cache_is_valid(cachefile):
return self.load(cachefile), True, time.time() - start
resp = self._request(url)
resp.raise_for_status()
try:
raw = resp.json()
except ValueError:
raise NoJSONResponseError()
self.save(raw, cachefile)
return raw, False, resp.elapsed.total_seconds()
Затем у меня есть другие классы и код, которые вызывают метод request
этого кода вот так :
class APIHelper():
def __init__(self):
self.cache = JSONRequestCacher()
def fetch(val):
url = 'my/url/{}'.format(val)
return self.cache.request(url)
def fetchall(vals):
repsonses = []
for val in vals:
responses.append(self.fetch(val))
return responses
Для небольшого количества vals
это нормально, и на самом деле нет ничего страшного в том, чтобы подождать 10 минут. Однако сейчас я собираюсь сделать 30 000+ обращений к этой конечной точке. Раньше я использовал пулы потоков (multiprocessing.dummy.Pool
) для достижения некоторого параллелизма, однако, по моему мнению, async/await
и aiothttp
- лучший способ для go. К сожалению, как бы я ни старался, я не могу понять, как перевести это в этот код. Я использую Python 3.8.
EDIT Я пробовал внести это изменение:
class JSONRequestCacher():
def __init__():
self.http = aiohttp.ClientSession()
async def _request(self, url):
async with self.http.get(url) as response:
return await response.read()
Получил ошибку: AttributeError: 'coroutine' object has no attribute 'json'
из моей raw = resp.json()
строки
Пытался добавить resp = await self._request(url)
, но это SyntaxError: 'await' outside async function
. Затем, если я создаю request
асинхронную c функцию, то ее вызов просто возвращает мне объект сопрограммы, который не дает мне ожидаемого ответа.
И это просто пытается сделать _request
вызов asyn c. Я даже не могу понять, как я должен делать несколько вызовов к нему через другой класс (APIHelper
).