-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Am K
committed
Sep 16, 2024
1 parent
c71d87e
commit 527c0c5
Showing
16 changed files
with
814 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,14 +10,14 @@ | |
|
||
setup( | ||
name='zcache', | ||
version='v1.0.2', | ||
version='v1.0.3', | ||
packages=['zcache',], | ||
license='MIT', | ||
author="guangrei", | ||
author_email="[email protected]", | ||
description="PyZCache is dependency free python key value cache based file storage and json serialize", | ||
description="Key Value Database/Cache with multiple storage and plugins", | ||
long_description=long_description, | ||
long_description_content_type='text/markdown', | ||
keywords="cache key value file json", | ||
url="https://github.com/guangrei/PyZCache", | ||
url="https://github.com/guangrei/zcache", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# -*-coding:utf8;-*- | ||
from zcache.Storage.BaseFileStorage import BaseFileStorage | ||
from zcache.Interface.Storage import Storage as StorageInterface | ||
from zcache.Interface.Plugins import Plugins as PluginsInterface | ||
import time | ||
|
||
|
||
class Database: | ||
|
||
def __init__( | ||
self, | ||
path=None, | ||
limit=0, | ||
storage=BaseFileStorage, | ||
plugins=None, | ||
StorageArgs=None, | ||
): | ||
if plugins is not None and not issubclass(plugins, PluginsInterface): | ||
raise NotImplementedError | ||
self.plugins = plugins | ||
if not issubclass(storage, StorageInterface): | ||
raise NotImplementedError | ||
if path is not None: | ||
path = path | ||
else: | ||
path = "zcache.json" | ||
if StorageArgs is not None: | ||
if isinstance(StorageArgs, dict): | ||
self.storage = storage(path, **StorageArgs) | ||
else: | ||
raise TypeError | ||
else: | ||
self.storage = storage(path) | ||
self.__limit = limit | ||
|
||
def __updatefile(self): | ||
self.storage.save(self.databases) | ||
|
||
def __loadfile(self): | ||
self.databases = self.storage.load() | ||
self.databases["limit"] = self.__limit | ||
|
||
def __exists(self, key): | ||
try: | ||
t = self.databases["data"][key] | ||
if t["ttl"] != 0: | ||
sisa = int(time.time()) - t["time"] | ||
if sisa >= t["ttl"]: | ||
if self.plugins is not None: | ||
self.plugins.on_expired(self, key) | ||
del self.databases["data"][key] | ||
self.__updatefile() | ||
return False, False | ||
else: | ||
return True, t["content"] | ||
else: | ||
return True, t["content"] | ||
except KeyError: | ||
return False, False | ||
|
||
def __set(self, key, value, ttl=0): | ||
if self.plugins is not None: | ||
value = self.plugins.on_write(self, key, value) | ||
data = {} | ||
data["time"] = int(time.time()) | ||
data["ttl"] = int(ttl) | ||
data["content"] = value | ||
self.databases["data"][key] = data | ||
self.__updatefile() | ||
|
||
def has(self, key): | ||
if not isinstance(key, str): | ||
raise TypeError | ||
self.__loadfile() | ||
r, v = self.__exists(key) | ||
return r | ||
|
||
def get(self, key): | ||
if not isinstance(key, str): | ||
raise TypeError | ||
self.__loadfile() | ||
r, v = self.__exists(key) | ||
if r: | ||
if self.plugins is not None: | ||
return self.plugins.on_read(self, key, v) | ||
return v | ||
else: | ||
return None | ||
|
||
def set(self, key, value, ttl=0): | ||
if not isinstance(key, str): | ||
raise TypeError | ||
# to optimize, __loadfile() not called here because already called in size() | ||
size = self.size() | ||
if self.databases["limit"] != 0: | ||
if self.databases["limit"] == size: | ||
if self.plugins is not None: | ||
self.plugins.on_limit(self, key, value, ttl) | ||
return False | ||
else: | ||
self.__set(key, value, ttl) | ||
return True | ||
else: | ||
self.__set(key, value, ttl) | ||
return True | ||
|
||
def delete(self, key): | ||
if not isinstance(key, str): | ||
raise TypeError | ||
# to optimize, __loadfile() not called here because already called in has() | ||
check = self.has(key) | ||
if check: | ||
if self.plugins is not None: | ||
self.plugins.on_delete(self, key) | ||
del self.databases["data"][key] | ||
self.__updatefile() | ||
return True | ||
else: | ||
return False | ||
|
||
def size(self): | ||
self.__loadfile() | ||
ret = len(self.databases["data"]) | ||
return ret | ||
|
||
def reset(self): | ||
self.__loadfile() | ||
self.databases["data"] = {} | ||
self.__updatefile() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# -*-coding:utf8;-*- | ||
from zcache.Storage.BaseFileStorage import BaseFileStorage | ||
from zcache.Class.Database import Database | ||
import uuid | ||
|
||
|
||
class Queue: | ||
"""Implementasi FIFO Queue. | ||
Methods: | ||
- put(item): Menambahkan item ke dalam queue. | ||
- get(): Menghapus dan mengembalikan item pertama dari queue. | ||
- peek(): Melihat item pertama tanpa menghapusnya. | ||
- empty(): Mengecek apakah queue kosong. | ||
- size(): Mendapatkan jumlah item dalam queue. | ||
""" | ||
|
||
def __init__(self, path="queue.json", storage=BaseFileStorage): | ||
self.q = Database(path=path, storage=BaseFileStorage) | ||
self._stack_load() | ||
|
||
def put(self, item, id=str(uuid.uuid4())): | ||
"""Menambahkan item ke dalam queue.""" | ||
if not isinstance(id, str): | ||
raise TypeError | ||
if id == "__queue__": | ||
raise ValueError | ||
queue = self._stack_load() | ||
queue.append(id) | ||
self.q.set(id, item) | ||
self._stack_update(queue) | ||
return id | ||
|
||
def get(self): | ||
"""Menghapus dan mengembalikan item pertama dari queue.""" | ||
queue = self._stack_load() | ||
if len(queue) > 0: | ||
id = queue.pop(0) | ||
ret = self.q.get(id) | ||
self.q.delete(id) | ||
self._stack_update(queue) | ||
return ret | ||
else: | ||
return None | ||
|
||
def peek(self): | ||
"""Melihat item pertama tanpa menghapusnya.""" | ||
queue = self._stack_load() | ||
if len(queue) > 0: | ||
id = queue[0] | ||
return self.q.get(id) | ||
|
||
def _stack_load(self): | ||
if not self.q.has("__queue__"): | ||
self.q.set("__queue__", []) | ||
return [] | ||
return self.q.get("__queue__") | ||
|
||
def _stack_update(self, data): | ||
self.q.set("__queue__", data) | ||
|
||
def empty(self): | ||
"""Mengecek apakah queue kosong.""" | ||
queue = self._stack_load() | ||
return len(queue) == 0 | ||
|
||
def size(self): | ||
"""Mendapatkan jumlah item dalam queue.""" | ||
queue = self._stack_load() | ||
return len(queue) | ||
|
||
def exists(self, id): | ||
"""Mengecek id queue""" | ||
if not isinstance(id, str): | ||
raise TypeError | ||
queue = self._stack_load() | ||
return id in queue |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
The MIT License (MIT) | ||
Copyright (c) 2022 PyZCache https://github.com/guangrei/PyZCache | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. | ||
""" | ||
from zcache.Class.Database import Database | ||
from zcache.Plugins.BytesCachePlugins import BytesCachePlugins | ||
from urllib import request | ||
import os | ||
|
||
|
||
class SmartRequest: | ||
""" | ||
A class for making Smart HTTP requests with caching capabilities using PyZCache. | ||
""" | ||
|
||
def __init__(self, url, cache_path=None, cache_time=120, offline_ttl=604800): | ||
if not isinstance(url, str): | ||
cache_name = url.url | ||
else: | ||
cache_name = url | ||
cache = Database(path=cache_path, plugins=BytesCachePlugins) | ||
if cache.has(cache_name): | ||
self.response = cache.get(cache_name) | ||
self.is_loaded_from_cache = True | ||
else: | ||
r = self._makeRequest(url, cache_name, cache) | ||
if r != False: | ||
cache.set(cache_name, r, ttl=cache_time) | ||
cache.set(cache_name+"_offline", r, ttl=offline_ttl) | ||
self.response = r | ||
self.is_loaded_from_cache = False | ||
else: | ||
self.response = cache.get(cache_name+"_offline") | ||
self.is_loaded_from_cache = True | ||
|
||
def _makeRequest(self, url, cache_name, cache): | ||
if not isinstance(url, str): | ||
try: | ||
headers, body = url.get() | ||
assert type(body) == str | ||
return {"headers": headers, "body": body} | ||
except BaseException as e: | ||
if cache.has(cache_name+"_offline"): | ||
return False | ||
else: | ||
raise Exception(e) | ||
try: | ||
response = request.urlopen(url) | ||
headers, body = (dict(response.info()), response.read()) | ||
return {"headers": headers, "body": body.decode('utf-8')} | ||
except BaseException as e: | ||
if cache.has(cache_name+"_offline"): | ||
return False | ||
else: | ||
raise Exception(e) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# -*-coding:utf8;-*- | ||
from abc import ABC, abstractmethod | ||
|
||
|
||
class Plugins(ABC): | ||
|
||
@abstractmethod | ||
def on_write(db, key, value): | ||
""" | ||
trigger at Database.__set() | ||
return will affect on data content before write. | ||
""" | ||
pass | ||
|
||
@abstractmethod | ||
def on_read(db, key, value): | ||
""" | ||
trigger at Database.get() | ||
return will affect on data content after read. | ||
""" | ||
pass | ||
|
||
@abstractmethod | ||
def on_limit(db, key, value, ttl): | ||
""" | ||
trigger when Database limit reached on Database.set() | ||
return will not affect anything. | ||
""" | ||
pass | ||
|
||
@abstractmethod | ||
def on_expired(db, key): | ||
""" | ||
trigger when Database key time to live limit reached. | ||
return will not affect anything. | ||
""" | ||
pass | ||
|
||
@abstractmethod | ||
def on_delete(db, key): | ||
""" | ||
trigger on Database.delete() | ||
return will not affect anything. | ||
""" | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# -*-coding:utf8;-*- | ||
from abc import ABC, abstractmethod | ||
|
||
|
||
class Storage(ABC): | ||
@abstractmethod | ||
def __init__(self, path): | ||
pass | ||
|
||
@abstractmethod | ||
def load(self): | ||
pass | ||
|
||
@abstractmethod | ||
def save(self, data): | ||
pass |
Empty file.
Oops, something went wrong.