8.13. Gateway
EN: Gateway
PL: Bramka
Type: class
8.13.1. Pattern
8.13.2. Problem
import os
import requests
class HttpClient:
def get(self, url):
return requests.get(url).json()
def post(self, url, data):
return requests.post(url, data).json()
class HttpStub:
def get(self, url):
return {'status': 'ok'}
def post(self, url, data):
return {'status': 'created'}
if __name__ == '__main__':
STAGE = os.getenv('STAGE', default='production')
if STAGE == 'test':
http = HttpStub()
else:
http = HttpClient()
http.get('https://python3.info')
8.13.3. Solution
import os
import requests
class HttpClient:
def get(self, url):
return requests.get(url).json()
def post(self, url, data):
return requests.post(url, data).json()
class HttpStub:
def get(self, url):
return {'status': 'ok'}
def post(self, url, data):
return {'status': 'created'}
def gateway():
STAGE = os.getenv('STAGE', default='production')
if STAGE == 'test':
return HttpStub()
else:
return HttpClient()
if __name__ == '__main__':
http = gateway()
http.get('https://python3.info')
8.13.4. Use Case - 1
import logging
import os
from dataclasses import dataclass
from datetime import timedelta, datetime
import requests
logging.basicConfig(
level=logging.INFO,
format='"%(asctime).19s", "%(levelname)s", "%(message)s"'
)
log = logging.getLogger(__name__)
class Cache:
def __init__(self, expiration: timedelta = timedelta(days=1), location: str = '') -> None:
self.location = location
self.expiration = expiration
def get(self, key: str) -> str:
raise NotImplementedError
def set(self, key: str, value: str) -> None:
raise NotImplementedError
def is_valid(self, key: str) -> bool:
raise NotImplementedError
class FilesystemCache(Cache):
def __init__(self, location: str = 'tmp', *args, **kwargs) -> None:
self.location = location
super().__init__(*args, **kwargs)
if not os.path.isdir(self.location):
if os.path.isfile(self.location):
os.remove(self.location)
os.mkdir(self.location)
def _get_cache_path(self, key: str) -> str:
filename = key.replace('/', '-').replace(':', '').replace('--', '-')
return os.path.join(self.location, filename)
def get(self, key: str) -> str:
filename = self._get_cache_path(key)
if not os.path.isfile(filename):
raise FileNotFoundError
with open(filename, mode='r', encoding='utf-8') as file:
return file.read()
def set(self, key: str, value: str) -> None:
filename = self._get_cache_path(key)
if value is None:
raise ValueError('Value cannot be None')
with open(filename, mode='w', encoding='utf-8') as file:
file.write(value)
def is_valid(self, key):
filename = self._get_cache_path(key)
if not os.path.isfile(filename):
return False
last_modification = os.path.getmtime(filename)
last_modification = datetime.fromtimestamp(last_modification)
now = datetime.now()
if (now - last_modification) > self.expiration:
return False
else:
return True
@dataclass
class HTTPGateway:
cache: Cache
def get(self, url):
if not self.cache.is_valid(url):
log.info('Downloading...')
html = requests.get(url).text
self.cache.set(url, html)
log.info('Done.')
return self.cache.get(url)
if __name__ == '__main__':
cache = FilesystemCache(expiration=timedelta(seconds=2), location='tmp')
# cache = DatabaseCache(expiration=timedelta(minutes=2), location='database.sqlite')
# cache = MemoryCache(expiration=timedelta(minutes=2))
http = HTTPGateway(cache=cache)
html = http.get('https://python3.info')
print(html)