diff --git a/cache/lru_cache.py b/cache/lru_cache.py new file mode 100644 index 0000000..1e675ea --- /dev/null +++ b/cache/lru_cache.py @@ -0,0 +1,105 @@ +"""LRU Cache with logging options""" + + +import logging + +from singleton import Singleton + + +logger = logging.getLogger("LRUCache") +logger.setLevel(logging.DEBUG) +handler = logging.FileHandler("cache.log", 'w') +formatter = logging.Formatter("%(asctime)s\t%(levelname)s\t%(name)s\t%(message)s") +handler.setFormatter(formatter) +logger.addHandler(handler) + + +class LRUCache(Singleton): + """LRU Cache implementation""" + def __init__(self, limit=42): + self.limit = limit + self.cache = {} + self.head = None + self.tail = None + logger.debug("New LRUCache instance created") + + def get(self, key): + """Get value by key""" + if key not in self.cache: + logger.info("Got key='%s' which is not in cache", key) + return None + + node = self.cache[key] + self.__move_to_front(node) + logger.debug("Got key='%s' from cache", key) + return node.value + + def set(self, key, value): + """Set key-value""" + if key in self.cache: + node = self.cache[key] + node.value = value + logger.debug("Updated key='%s'", key) + self.__move_to_front(node) + else: + node = Node(key, value) + self.cache[key] = node + logger.debug("Added key='%s' to cache", key) + self.__add_to_front(node) + + if len(self.cache) > self.limit: + logger.info("Removing last key...") + self.__remove_last() + + def __add_to_front(self, node): + if not self.head: + self.head = node + self.tail = node + else: + node.next = self.head + self.head.prev = node + self.head = node + + def __remove_last(self): + if not self.tail: + return + + last_key = self.tail.key + last_elem = self.cache.pop(self.tail.key).value + logger.debug("Removed key='%s', value='%s'", last_key, last_elem) + + if self.head == self.tail: + self.head = None + self.tail = None + else: + self.tail = self.tail.prev + self.tail.next = None + + def __move_to_front(self, node): + if node == self.head: + return + + if node == self.tail: + self.tail = node.prev + self.tail.next = None + else: + node.prev.next = node.next + node.next.prev = node.prev + + node.prev = None + node.next = self.head + self.head.prev = node + self.head = node + + logger.info("Moved to front key='%s', value='%s'", self.head.key, self.head.value) + + +class Node: + """Subclass for LRUCache""" + def __init__(self, key, value): + self.key = key + self.value = value + self.prev = None + self.next = None + + logger.debug("New Node instance created") diff --git a/cache/lru_decorator.py b/cache/lru_decorator.py new file mode 100644 index 0000000..1364d95 --- /dev/null +++ b/cache/lru_decorator.py @@ -0,0 +1,18 @@ +from lru_cache import LRUCache + + +def lru_cache(limit): + lru = LRUCache(limit) + + def decorator(func): + def inner(*args, **kwargs): + key = func + if key in lru.cache: + return lru.get(key) + res = func(*args, **kwargs) + lru.set(key, res) + return res + + return inner + + return decorator diff --git a/cache/singleton.py b/cache/singleton.py new file mode 100644 index 0000000..c9a639f --- /dev/null +++ b/cache/singleton.py @@ -0,0 +1,5 @@ +class Singleton(object): + def __new__(cls, *args): + if not hasattr(cls, 'instance'): + cls.instance = super(Singleton, cls).__new__(cls) + return cls.instance diff --git a/cache/ttl_cache.py b/cache/ttl_cache.py new file mode 100644 index 0000000..cbfab2b --- /dev/null +++ b/cache/ttl_cache.py @@ -0,0 +1,15 @@ +from lru_cache import LRUCache + + +def ttl_cache(ttl, limit): + lru = LRUCache(limit) + + def decorator(func): + def inner(path, method): + key = str(path) + str(method) + if key in lru.cache: + return lru.get(key) + res = func(path, method) + return res + return inner + return decorator diff --git a/client/client.py b/client/client.py new file mode 100644 index 0000000..31d2a0f --- /dev/null +++ b/client/client.py @@ -0,0 +1,32 @@ +import http.client +from http.client import HTTPConnection +from http import HTTPStatus +import logging + + +logging.basicConfig(level=logging.DEBUG) + + +class Client: + def __init__(self): + self.connection = None + + def set_connection(self, host, port=None): + self.connection = HTTPConnection(host, port) + self.__check_connection() + + def __check_connection(self): + self.connection.request("GET", "/") + response = self.connection.getresponse() + response.read() + if response.status == 200: + logging.debug("Connected successfully") + else: + logging.warning(f"Status: {response.status}, reason: {response.reason}") + + def request(self, method: str, url: str, body=None, headers: dict = {}) -> None: + self.connection.request(method, url, body, headers) + logging.debug(f"Request sent: {method}, {url}") + + def get_response(self) -> http.client.HTTPResponse: + return self.connection.getresponse()