Kekik 1.6.9__tar.gz → 1.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/cache.py +155 -55
- {kekik-1.6.9 → kekik-1.7.1}/Kekik.egg-info/PKG-INFO +1 -1
- {kekik-1.6.9 → kekik-1.7.1}/PKG-INFO +1 -1
- {kekik-1.6.9 → kekik-1.7.1}/setup.py +1 -1
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/BIST.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/Domain2IP.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/Nesne.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/Sifreleme/AESManager.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/Sifreleme/CryptoJS.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/Sifreleme/HexCodec.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/Sifreleme/NaysHash.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/Sifreleme/Packer.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/Sifreleme/StringCodec.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/Sifreleme/__init__.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/__init__.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/cli.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/csv2dict.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/dict2csv.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/dict2json.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/dosya2set.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/dosya_indir.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/hwid_kontrol.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/kisi_ver/__init__.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/kisi_ver/biyografiler.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/kisi_ver/isimler.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/kisi_ver/soyisimler.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/link_islemleri.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/list2html.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/liste_fetis.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/mail_gonder.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/okunabilir_byte.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/proxy_ver.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/qr_ver.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/ses_fetis.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/slugify.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/terminal_baslik.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/txt_fetis.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/unicode_tr.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik/zaman_donustur.py +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik.egg-info/SOURCES.txt +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik.egg-info/dependency_links.txt +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik.egg-info/entry_points.txt +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik.egg-info/requires.txt +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/Kekik.egg-info/top_level.txt +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/LICENSE +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/MANIFEST.in +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/README.md +0 -0
- {kekik-1.6.9 → kekik-1.7.1}/setup.cfg +0 -0
|
@@ -1,25 +1,94 @@
|
|
|
1
1
|
# ! https://github.com/Fatal1ty/aiocached
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
from .cli import konsol
|
|
4
4
|
from functools import wraps
|
|
5
|
-
from time import time
|
|
5
|
+
from time import time, sleep
|
|
6
6
|
from hashlib import md5
|
|
7
7
|
from urllib.parse import urlencode
|
|
8
|
+
import asyncio, threading
|
|
9
|
+
|
|
10
|
+
# -----------------------------------------------------
|
|
11
|
+
# Yardımcı Fonksiyonlar
|
|
12
|
+
# -----------------------------------------------------
|
|
8
13
|
|
|
9
14
|
UNLIMITED = None
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
def normalize_for_key(value):
|
|
12
17
|
"""
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
Cache key oluşturma amacıyla verilen değeri normalize eder.
|
|
19
|
+
- Basit tipler (int, float, str, bool, None) direk kullanılır.
|
|
20
|
+
- dict: Anahtarları sıralı olarak normalize eder.
|
|
21
|
+
- list/tuple: Elemanları normalize eder.
|
|
22
|
+
- Diğer: Sadece sınıf ismi kullanılır.
|
|
15
23
|
"""
|
|
24
|
+
if isinstance(value, (int, float, str, bool, type(None))):
|
|
25
|
+
return value
|
|
26
|
+
|
|
27
|
+
elif isinstance(value, dict):
|
|
28
|
+
return {k: normalize_for_key(value[k]) for k in sorted(value)}
|
|
29
|
+
|
|
30
|
+
elif isinstance(value, (list, tuple)):
|
|
31
|
+
return [normalize_for_key(item) for item in value]
|
|
32
|
+
|
|
33
|
+
else:
|
|
34
|
+
return value.__class__.__name__
|
|
35
|
+
|
|
36
|
+
def simple_cache_key(func, args, kwargs) -> str:
|
|
37
|
+
"""
|
|
38
|
+
Fonksiyonun tam adı ve parametrelerini kullanarak bir cache key oluşturur.
|
|
39
|
+
Oluşturulan stringin sonuna MD5 hash eklenir.
|
|
40
|
+
"""
|
|
41
|
+
base_key = f"{func.__module__}.{func.__qualname__}"
|
|
42
|
+
|
|
43
|
+
if args:
|
|
44
|
+
norm_args = [normalize_for_key(arg) for arg in args]
|
|
45
|
+
base_key += f"|{norm_args}"
|
|
46
|
+
|
|
47
|
+
if kwargs:
|
|
48
|
+
norm_kwargs = {k: normalize_for_key(v) for k, v in kwargs.items()}
|
|
49
|
+
base_key += f"|{str(sorted(norm_kwargs.items()))}"
|
|
50
|
+
|
|
51
|
+
hashed = md5(base_key.encode('utf-8')).hexdigest()
|
|
52
|
+
return f"{base_key}" # |{hashed}
|
|
53
|
+
|
|
54
|
+
async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
|
|
55
|
+
"""
|
|
56
|
+
Cache key'ini oluşturur.
|
|
57
|
+
- is_fastapi=False ise simple_cache_key() kullanılır.
|
|
58
|
+
- True ise FastAPI Request nesnesine göre özel key oluşturulur.
|
|
59
|
+
"""
|
|
60
|
+
if not is_fastapi:
|
|
61
|
+
return simple_cache_key(func, args, kwargs)
|
|
62
|
+
|
|
63
|
+
# FastAPI: request ilk argüman ya da kwargs'dan alınır
|
|
64
|
+
request = args[0] if args else kwargs.get("request")
|
|
65
|
+
|
|
66
|
+
if request.method == "GET":
|
|
67
|
+
# Eğer query_params boşsa {} olarak ayarla
|
|
68
|
+
veri = dict(request.query_params) if request.query_params else {}
|
|
69
|
+
else:
|
|
70
|
+
try:
|
|
71
|
+
veri = await request.json()
|
|
72
|
+
except Exception:
|
|
73
|
+
form_data = await request.form()
|
|
74
|
+
veri = dict(form_data.items())
|
|
75
|
+
|
|
76
|
+
args_hash = md5(urlencode(veri).encode()).hexdigest() if veri else ""
|
|
77
|
+
return f"{request.url.path}?{veri}"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# -----------------------------------------------------
|
|
81
|
+
# In-Memory (RAM) Cache
|
|
82
|
+
# -----------------------------------------------------
|
|
83
|
+
|
|
84
|
+
class Cache:
|
|
16
85
|
def __init__(self, ttl=UNLIMITED):
|
|
17
|
-
self._data = {}
|
|
18
86
|
self._ttl = ttl
|
|
87
|
+
self._data = {}
|
|
19
88
|
self._times = {}
|
|
20
89
|
|
|
21
90
|
def _is_expired(self, key):
|
|
22
|
-
"""Belirtilen key'in süresi
|
|
91
|
+
"""Belirtilen key'in süresi dolmuşsa True döner."""
|
|
23
92
|
if self._ttl is UNLIMITED:
|
|
24
93
|
return False
|
|
25
94
|
|
|
@@ -27,22 +96,57 @@ class Cache:
|
|
|
27
96
|
|
|
28
97
|
return timestamp is not None and (time() - timestamp > self._ttl)
|
|
29
98
|
|
|
99
|
+
|
|
100
|
+
class SyncCache(Cache):
|
|
101
|
+
"""
|
|
102
|
+
Basit in-memory cache yapısı.
|
|
103
|
+
TTL (time-to-live) süresi dolan veriler otomatik olarak temizlenir.
|
|
104
|
+
Bu versiyonda, otomatik temizleme işlevselliği bir thread ile sağlanır.
|
|
105
|
+
"""
|
|
106
|
+
def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60):
|
|
107
|
+
super().__init__(ttl)
|
|
108
|
+
|
|
109
|
+
# TTL sınırsız değilse, cleanup_interval ile ttl'den büyük olanı kullanıyoruz.
|
|
110
|
+
self._cleanup_interval = max(ttl, cleanup_interval) if ttl is not UNLIMITED else cleanup_interval
|
|
111
|
+
self._lock = threading.RLock()
|
|
112
|
+
|
|
113
|
+
# Arka planda çalışan ve periyodik olarak expired entry'leri temizleyen thread başlatılıyor.
|
|
114
|
+
self._cleanup_thread = threading.Thread(target=self._auto_cleanup, daemon=True)
|
|
115
|
+
self._cleanup_thread.start()
|
|
116
|
+
|
|
117
|
+
def _auto_cleanup(self):
|
|
118
|
+
"""Belirlenen aralıklarla cache içerisindeki süresi dolmuş entry'leri temizler."""
|
|
119
|
+
while True:
|
|
120
|
+
sleep(self._cleanup_interval)
|
|
121
|
+
with self._lock:
|
|
122
|
+
keys = list(self._data.keys())
|
|
123
|
+
for key in keys:
|
|
124
|
+
self.remove_if_expired(key)
|
|
125
|
+
|
|
30
126
|
def remove_if_expired(self, key):
|
|
31
127
|
"""
|
|
32
128
|
Eğer key'in cache süresi dolmuşsa, ilgili entry'yi temizler.
|
|
129
|
+
Thread güvenliği sağlamak için lock kullanılır.
|
|
33
130
|
"""
|
|
34
|
-
|
|
35
|
-
self.
|
|
36
|
-
|
|
131
|
+
with self._lock:
|
|
132
|
+
if self._is_expired(key):
|
|
133
|
+
self._data.pop(key, None)
|
|
134
|
+
self._times.pop(key, None)
|
|
135
|
+
# konsol.log(f"[red][-] {key}")
|
|
37
136
|
|
|
38
137
|
def __getitem__(self, key):
|
|
39
|
-
self.
|
|
40
|
-
|
|
138
|
+
with self._lock:
|
|
139
|
+
self.remove_if_expired(key)
|
|
140
|
+
veri = self._data[key]
|
|
141
|
+
# konsol.log(f"[yellow][~] {key}")
|
|
142
|
+
return veri
|
|
41
143
|
|
|
42
144
|
def __setitem__(self, key, value):
|
|
43
|
-
self.
|
|
44
|
-
|
|
45
|
-
|
|
145
|
+
with self._lock:
|
|
146
|
+
self._data[key] = value
|
|
147
|
+
# konsol.log(f"[green][+] {key}")
|
|
148
|
+
if self._ttl is not UNLIMITED:
|
|
149
|
+
self._times[key] = time()
|
|
46
150
|
|
|
47
151
|
|
|
48
152
|
class AsyncCache(Cache):
|
|
@@ -75,20 +179,33 @@ class AsyncCache(Cache):
|
|
|
75
179
|
for key in keys:
|
|
76
180
|
self.remove_if_expired(key)
|
|
77
181
|
|
|
182
|
+
def ensure_cleanup_task(self):
|
|
183
|
+
"""Event loop mevcutsa, cleanup task henüz başlatılmadıysa oluştur."""
|
|
184
|
+
if self._cleanup_task is None:
|
|
185
|
+
try:
|
|
186
|
+
self._cleanup_task = asyncio.get_running_loop().create_task(self._auto_cleanup())
|
|
187
|
+
except RuntimeError:
|
|
188
|
+
pass # Yine loop yoksa yapacak bir şey yok
|
|
189
|
+
|
|
78
190
|
async def get(self, key):
|
|
79
191
|
"""
|
|
80
192
|
Belirtilen key için cache'de saklanan değeri asenkron olarak döndürür.
|
|
81
193
|
Eğer key bulunamazsa, ilgili future üzerinden beklemeyi sağlar.
|
|
82
194
|
"""
|
|
195
|
+
self.ensure_cleanup_task()
|
|
83
196
|
self.remove_if_expired(key)
|
|
84
197
|
|
|
85
198
|
try:
|
|
86
|
-
|
|
199
|
+
veri = self._data[key]
|
|
200
|
+
# konsol.log(f"[yellow][~] {key}")
|
|
201
|
+
return veri
|
|
87
202
|
except KeyError as e:
|
|
88
203
|
future = self.futures.get(key)
|
|
89
204
|
if future:
|
|
90
205
|
await future
|
|
91
|
-
|
|
206
|
+
veri = future.result()
|
|
207
|
+
# konsol.log(f"[yellow][?] {key}")
|
|
208
|
+
return veri
|
|
92
209
|
|
|
93
210
|
raise e
|
|
94
211
|
|
|
@@ -97,16 +214,26 @@ class AsyncCache(Cache):
|
|
|
97
214
|
Belirtilen key'in süresi dolduysa, cache ve futures içerisinden temizler.
|
|
98
215
|
"""
|
|
99
216
|
if self._ttl is not UNLIMITED and self._is_expired(key):
|
|
217
|
+
# konsol.log(f"[red][-] {key}")
|
|
100
218
|
self._data.pop(key, None)
|
|
101
219
|
self._times.pop(key, None)
|
|
102
220
|
self.futures.pop(key, None)
|
|
103
221
|
|
|
222
|
+
def __setitem__(self, key, value):
|
|
223
|
+
self._data[key] = value
|
|
224
|
+
if self._ttl is not UNLIMITED:
|
|
225
|
+
self._times[key] = time()
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# -----------------------------------------------------
|
|
229
|
+
# Fonksiyonun Sonucunu Hesaplayıp Cache'e Yazma
|
|
230
|
+
# -----------------------------------------------------
|
|
104
231
|
|
|
105
232
|
def _sync_maybe_cache(func, key, result, unless):
|
|
106
233
|
"""Senkron sonuç için cache kaydını oluşturur (unless koşuluna bakarak)."""
|
|
107
234
|
if unless is None or not unless(result):
|
|
108
235
|
func.__cache[key] = result
|
|
109
|
-
|
|
236
|
+
# konsol.log(f"[green][+] {key}")
|
|
110
237
|
|
|
111
238
|
async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
|
|
112
239
|
"""
|
|
@@ -133,6 +260,7 @@ async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
|
|
|
133
260
|
# unless koşuluna göre cache'e ekleme yap.
|
|
134
261
|
if unless is None or not unless(result):
|
|
135
262
|
cache[key] = result
|
|
263
|
+
# konsol.log(f"[green][+] {key}")
|
|
136
264
|
|
|
137
265
|
return result
|
|
138
266
|
except Exception as exc:
|
|
@@ -142,34 +270,10 @@ async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
|
|
|
142
270
|
# İşlem tamamlandığında future'ı temizle.
|
|
143
271
|
cache.futures.pop(key, None)
|
|
144
272
|
|
|
145
|
-
async def make_cache_key(args, kwargs, is_fastapi=False):
|
|
146
|
-
"""
|
|
147
|
-
Cache key'ini oluşturur.
|
|
148
|
-
|
|
149
|
-
:param is_fastapi (bool): Eğer True ise, ilk argümanın bir FastAPI Request nesnesi olduğu varsayılır.
|
|
150
|
-
Bu durumda, cache key, request nesnesinin URL yolunu (request.url.path) ve
|
|
151
|
-
isteğe ait verilerden (GET istekleri için query parametreleri; diğer istekler için JSON veya form verileri)
|
|
152
|
-
elde edilen verinin URL uyumlu halinin md5 hash'inin birleşiminden oluşturulur.
|
|
153
|
-
Böylece, aynı URL ve aynı istek verileri için her seferinde aynı cache key üretilecektir.
|
|
154
|
-
Eğer False ise, cache key args ve kwargs değerlerinden, sıralı bir tuple olarak oluşturulur.
|
|
155
|
-
"""
|
|
156
|
-
if not is_fastapi:
|
|
157
|
-
return (args, tuple(sorted(kwargs.items())))
|
|
158
|
-
|
|
159
|
-
request = args[0] if args else kwargs.get("request")
|
|
160
|
-
|
|
161
|
-
if request.method == "GET":
|
|
162
|
-
veri = dict(request.query_params) if request.query_params else None
|
|
163
|
-
else:
|
|
164
|
-
try:
|
|
165
|
-
veri = await request.json()
|
|
166
|
-
except Exception:
|
|
167
|
-
form_data = await request.form()
|
|
168
|
-
veri = dict(form_data.items())
|
|
169
|
-
|
|
170
|
-
args_hash = md5(urlencode(veri).encode()).hexdigest() if veri else ""
|
|
171
|
-
return f"{request.url.path}?{args_hash}"
|
|
172
273
|
|
|
274
|
+
# -----------------------------------------------------
|
|
275
|
+
# Dekoratör: kekik_cache
|
|
276
|
+
# -----------------------------------------------------
|
|
173
277
|
|
|
174
278
|
def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False):
|
|
175
279
|
"""
|
|
@@ -203,13 +307,11 @@ def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False):
|
|
|
203
307
|
|
|
204
308
|
def decorator(func):
|
|
205
309
|
if asyncio.iscoroutinefunction(func):
|
|
310
|
+
func.__cache = AsyncCache(ttl)
|
|
311
|
+
|
|
206
312
|
@wraps(func)
|
|
207
313
|
async def async_wrapper(*args, **kwargs):
|
|
208
|
-
|
|
209
|
-
if not hasattr(func, "__cache"):
|
|
210
|
-
func.__cache = AsyncCache(ttl)
|
|
211
|
-
|
|
212
|
-
key = await make_cache_key(args, kwargs, is_fastapi)
|
|
314
|
+
key = await make_cache_key(func, args, kwargs, is_fastapi)
|
|
213
315
|
|
|
214
316
|
try:
|
|
215
317
|
return await func.__cache.get(key)
|
|
@@ -218,13 +320,11 @@ def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False):
|
|
|
218
320
|
|
|
219
321
|
return async_wrapper
|
|
220
322
|
else:
|
|
323
|
+
func.__cache = SyncCache(ttl)
|
|
324
|
+
|
|
221
325
|
@wraps(func)
|
|
222
326
|
def sync_wrapper(*args, **kwargs):
|
|
223
|
-
|
|
224
|
-
if not hasattr(func, "__cache"):
|
|
225
|
-
func.__cache = AsyncCache(ttl)
|
|
226
|
-
|
|
227
|
-
key = (args, tuple(sorted(kwargs.items())))
|
|
327
|
+
key = simple_cache_key(func, args, kwargs)
|
|
228
328
|
|
|
229
329
|
try:
|
|
230
330
|
return func.__cache[key]
|
|
@@ -6,7 +6,7 @@ from io import open
|
|
|
6
6
|
setup(
|
|
7
7
|
# ? Genel Bilgiler
|
|
8
8
|
name = "Kekik",
|
|
9
|
-
version = "1.
|
|
9
|
+
version = "1.7.1",
|
|
10
10
|
url = "https://github.com/keyiflerolsun/Kekik",
|
|
11
11
|
description = "İşlerimizi kolaylaştıracak fonksiyonların el altında durduğu kütüphane..",
|
|
12
12
|
keywords = ["Kekik", "KekikAkademi", "keyiflerolsun"],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|