Kekik 1.7.1__tar.gz → 1.7.2__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.
Potentially problematic release.
This version of Kekik might be problematic. Click here for more details.
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/cache.py +168 -96
- {kekik-1.7.1 → kekik-1.7.2}/Kekik.egg-info/PKG-INFO +1 -1
- {kekik-1.7.1 → kekik-1.7.2}/PKG-INFO +1 -1
- {kekik-1.7.1 → kekik-1.7.2}/setup.py +1 -1
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/BIST.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/Domain2IP.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/Nesne.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/Sifreleme/AESManager.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/Sifreleme/CryptoJS.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/Sifreleme/HexCodec.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/Sifreleme/NaysHash.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/Sifreleme/Packer.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/Sifreleme/StringCodec.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/Sifreleme/__init__.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/__init__.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/cli.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/csv2dict.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/dict2csv.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/dict2json.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/dosya2set.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/dosya_indir.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/hwid_kontrol.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/kisi_ver/__init__.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/kisi_ver/biyografiler.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/kisi_ver/isimler.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/kisi_ver/soyisimler.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/link_islemleri.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/list2html.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/liste_fetis.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/mail_gonder.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/okunabilir_byte.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/proxy_ver.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/qr_ver.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/ses_fetis.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/slugify.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/terminal_baslik.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/txt_fetis.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/unicode_tr.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik/zaman_donustur.py +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik.egg-info/SOURCES.txt +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik.egg-info/dependency_links.txt +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik.egg-info/entry_points.txt +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik.egg-info/requires.txt +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/Kekik.egg-info/top_level.txt +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/LICENSE +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/MANIFEST.in +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/README.md +0 -0
- {kekik-1.7.1 → kekik-1.7.2}/setup.cfg +0 -0
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
|
|
2
2
|
|
|
3
3
|
from .cli import konsol
|
|
4
4
|
from functools import wraps
|
|
5
|
-
from time import time, sleep
|
|
6
5
|
from hashlib import md5
|
|
7
6
|
from urllib.parse import urlencode
|
|
8
|
-
import
|
|
7
|
+
import time
|
|
8
|
+
import threading
|
|
9
|
+
import asyncio
|
|
10
|
+
import pickle
|
|
11
|
+
|
|
12
|
+
# Redis için asenkron client (Redis yoksa fallback yapılacak)
|
|
13
|
+
import redis.asyncio as redis
|
|
9
14
|
|
|
10
15
|
# -----------------------------------------------------
|
|
11
|
-
# Yardımcı Fonksiyonlar
|
|
16
|
+
# Sabitler ve Yardımcı Fonksiyonlar
|
|
12
17
|
# -----------------------------------------------------
|
|
13
|
-
|
|
14
18
|
UNLIMITED = None
|
|
15
19
|
|
|
16
20
|
def normalize_for_key(value):
|
|
17
21
|
"""
|
|
18
22
|
Cache key oluşturma amacıyla verilen değeri normalize eder.
|
|
19
|
-
- Basit tipler (int, float, str, bool, None)
|
|
23
|
+
- Basit tipler (int, float, str, bool, None) doğrudan kullanılır.
|
|
20
24
|
- dict: Anahtarları sıralı olarak normalize eder.
|
|
21
25
|
- list/tuple: Elemanları normalize eder.
|
|
22
26
|
- Diğer: Sadece sınıf ismi kullanılır.
|
|
@@ -36,7 +40,7 @@ def normalize_for_key(value):
|
|
|
36
40
|
def simple_cache_key(func, args, kwargs) -> str:
|
|
37
41
|
"""
|
|
38
42
|
Fonksiyonun tam adı ve parametrelerini kullanarak bir cache key oluşturur.
|
|
39
|
-
Oluşturulan stringin sonuna MD5 hash
|
|
43
|
+
Oluşturulan stringin sonuna MD5 hash eklenebilir.
|
|
40
44
|
"""
|
|
41
45
|
base_key = f"{func.__module__}.{func.__qualname__}"
|
|
42
46
|
|
|
@@ -60,7 +64,7 @@ async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
|
|
|
60
64
|
if not is_fastapi:
|
|
61
65
|
return simple_cache_key(func, args, kwargs)
|
|
62
66
|
|
|
63
|
-
# FastAPI: request ilk
|
|
67
|
+
# FastAPI: request ilk argümandan ya da kwargs'dan alınır.
|
|
64
68
|
request = args[0] if args else kwargs.get("request")
|
|
65
69
|
|
|
66
70
|
if request.method == "GET":
|
|
@@ -73,56 +77,52 @@ async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
|
|
|
73
77
|
form_data = await request.form()
|
|
74
78
|
veri = dict(form_data.items())
|
|
75
79
|
|
|
80
|
+
# Eğer "kurek" gibi özel parametreler varsa temizleyebilirsiniz:
|
|
81
|
+
veri.pop("kurek", None)
|
|
82
|
+
|
|
76
83
|
args_hash = md5(urlencode(veri).encode()).hexdigest() if veri else ""
|
|
77
84
|
return f"{request.url.path}?{veri}"
|
|
78
85
|
|
|
79
|
-
|
|
80
86
|
# -----------------------------------------------------
|
|
81
|
-
#
|
|
87
|
+
# Senkron Cache (RAM) Sınıfı
|
|
82
88
|
# -----------------------------------------------------
|
|
83
89
|
|
|
84
|
-
class
|
|
85
|
-
def __init__(self, ttl=UNLIMITED):
|
|
86
|
-
self._ttl = ttl
|
|
87
|
-
self._data = {}
|
|
88
|
-
self._times = {}
|
|
89
|
-
|
|
90
|
-
def _is_expired(self, key):
|
|
91
|
-
"""Belirtilen key'in süresi dolmuşsa True döner."""
|
|
92
|
-
if self._ttl is UNLIMITED:
|
|
93
|
-
return False
|
|
94
|
-
|
|
95
|
-
timestamp = self._times.get(key)
|
|
96
|
-
|
|
97
|
-
return timestamp is not None and (time() - timestamp > self._ttl)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
class SyncCache(Cache):
|
|
90
|
+
class SyncCache:
|
|
101
91
|
"""
|
|
102
|
-
|
|
103
|
-
TTL
|
|
104
|
-
Bu versiyonda, otomatik temizleme işlevselliği bir thread ile sağlanır.
|
|
92
|
+
Senkron fonksiyonlar için basit in-memory cache.
|
|
93
|
+
TTL süresi dolan veriler periyodik olarak arka plan thread’iyle temizlenir.
|
|
105
94
|
"""
|
|
106
95
|
def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60):
|
|
107
|
-
|
|
96
|
+
self._ttl = ttl
|
|
97
|
+
self._data = {}
|
|
98
|
+
self._times = {}
|
|
108
99
|
|
|
109
100
|
# TTL sınırsız değilse, cleanup_interval ile ttl'den büyük olanı kullanıyoruz.
|
|
110
101
|
self._cleanup_interval = max(ttl, cleanup_interval) if ttl is not UNLIMITED else cleanup_interval
|
|
111
|
-
self._lock = threading.RLock()
|
|
112
102
|
|
|
113
103
|
# Arka planda çalışan ve periyodik olarak expired entry'leri temizleyen thread başlatılıyor.
|
|
104
|
+
self._lock = threading.RLock()
|
|
114
105
|
self._cleanup_thread = threading.Thread(target=self._auto_cleanup, daemon=True)
|
|
115
106
|
self._cleanup_thread.start()
|
|
116
107
|
|
|
117
108
|
def _auto_cleanup(self):
|
|
118
109
|
"""Belirlenen aralıklarla cache içerisindeki süresi dolmuş entry'leri temizler."""
|
|
119
110
|
while True:
|
|
120
|
-
sleep(self._cleanup_interval)
|
|
111
|
+
time.sleep(self._cleanup_interval)
|
|
121
112
|
with self._lock:
|
|
122
113
|
keys = list(self._data.keys())
|
|
123
114
|
for key in keys:
|
|
124
115
|
self.remove_if_expired(key)
|
|
125
116
|
|
|
117
|
+
def _is_expired(self, key):
|
|
118
|
+
"""Belirtilen key'in süresi dolmuşsa True döner."""
|
|
119
|
+
if self._ttl is UNLIMITED:
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
timestamp = self._times.get(key)
|
|
123
|
+
|
|
124
|
+
return timestamp is not None and (time.time() - timestamp > self._ttl)
|
|
125
|
+
|
|
126
126
|
def remove_if_expired(self, key):
|
|
127
127
|
"""
|
|
128
128
|
Eğer key'in cache süresi dolmuşsa, ilgili entry'yi temizler.
|
|
@@ -144,26 +144,30 @@ class SyncCache(Cache):
|
|
|
144
144
|
def __setitem__(self, key, value):
|
|
145
145
|
with self._lock:
|
|
146
146
|
self._data[key] = value
|
|
147
|
-
# konsol.log(f"[green][+] {key}")
|
|
148
147
|
if self._ttl is not UNLIMITED:
|
|
149
|
-
self._times[key] = time()
|
|
148
|
+
self._times[key] = time.time()
|
|
150
149
|
|
|
150
|
+
# -----------------------------------------------------
|
|
151
|
+
# Asenkron Cache (In-Memory) ve Redis Hybrid Cache
|
|
152
|
+
# -----------------------------------------------------
|
|
151
153
|
|
|
152
|
-
class AsyncCache
|
|
154
|
+
class AsyncCache:
|
|
153
155
|
"""
|
|
154
|
-
|
|
155
|
-
Aynı key için gelen eşzamanlı çağrılar, futures kullanılarak tek sonuç üzerinden paylaşılır.
|
|
156
|
-
Ek olarak, belirli aralıklarla cache’i kontrol edip, süresi dolmuş verileri temizleyen otomatik temizleme görevi çalışır.
|
|
156
|
+
Temel in-memory asenkron cache.
|
|
157
157
|
"""
|
|
158
158
|
def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60):
|
|
159
159
|
"""
|
|
160
160
|
:param ttl: Her entry için geçerli süre (saniye). Örneğin 3600 saniye 1 saattir.
|
|
161
161
|
:param cleanup_interval: Otomatik temizleme görevinin kaç saniyede bir çalışacağını belirler.
|
|
162
162
|
"""
|
|
163
|
-
|
|
163
|
+
self._ttl = ttl
|
|
164
|
+
self._data = {}
|
|
165
|
+
self._times = {}
|
|
164
166
|
self.futures = {}
|
|
165
167
|
|
|
168
|
+
# TTL sınırsız değilse, cleanup_interval ile ttl'den büyük olanı kullanıyoruz.
|
|
166
169
|
self._cleanup_interval = max(ttl, cleanup_interval) if ttl is not UNLIMITED else cleanup_interval
|
|
170
|
+
|
|
167
171
|
# Aktif bir event loop varsa otomatik temizlik görevini başlatıyoruz.
|
|
168
172
|
try:
|
|
169
173
|
self._cleanup_task = asyncio.get_running_loop().create_task(self._auto_cleanup())
|
|
@@ -174,9 +178,7 @@ class AsyncCache(Cache):
|
|
|
174
178
|
"""Belirlenen aralıklarla cache içerisindeki süresi dolmuş entry'leri temizler."""
|
|
175
179
|
while True:
|
|
176
180
|
await asyncio.sleep(self._cleanup_interval)
|
|
177
|
-
|
|
178
|
-
keys = list(self._data.keys())
|
|
179
|
-
for key in keys:
|
|
181
|
+
for key in list(self._data.keys()):
|
|
180
182
|
self.remove_if_expired(key)
|
|
181
183
|
|
|
182
184
|
def ensure_cleanup_task(self):
|
|
@@ -187,59 +189,137 @@ class AsyncCache(Cache):
|
|
|
187
189
|
except RuntimeError:
|
|
188
190
|
pass # Yine loop yoksa yapacak bir şey yok
|
|
189
191
|
|
|
192
|
+
def remove_if_expired(self, key):
|
|
193
|
+
"""
|
|
194
|
+
Belirtilen key'in süresi dolduysa, cache ve futures içerisinden temizler.
|
|
195
|
+
"""
|
|
196
|
+
if self._ttl is not UNLIMITED:
|
|
197
|
+
t = self._times.get(key)
|
|
198
|
+
if t is not None and (time.time() - t > self._ttl):
|
|
199
|
+
# konsol.log(f"[red][-] {key}")
|
|
200
|
+
self._data.pop(key, None)
|
|
201
|
+
self._times.pop(key, None)
|
|
202
|
+
self.futures.pop(key, None)
|
|
203
|
+
|
|
190
204
|
async def get(self, key):
|
|
191
205
|
"""
|
|
192
206
|
Belirtilen key için cache'de saklanan değeri asenkron olarak döndürür.
|
|
193
207
|
Eğer key bulunamazsa, ilgili future üzerinden beklemeyi sağlar.
|
|
194
208
|
"""
|
|
209
|
+
# Cleanup task'in aktif olduğundan emin olun.
|
|
195
210
|
self.ensure_cleanup_task()
|
|
211
|
+
# Eğer key'in süresi dolmuşsa, kaldırın.
|
|
196
212
|
self.remove_if_expired(key)
|
|
197
213
|
|
|
198
214
|
try:
|
|
199
|
-
|
|
215
|
+
# Cache içerisinde key varsa direkt değeri döndür.
|
|
216
|
+
value = self._data[key]
|
|
200
217
|
# konsol.log(f"[yellow][~] {key}")
|
|
201
|
-
return
|
|
202
|
-
except KeyError
|
|
218
|
+
return value
|
|
219
|
+
except KeyError:
|
|
220
|
+
# Eğer key cache'de yoksa, aynı key ile başlatılmış future varsa onu bekle.
|
|
203
221
|
future = self.futures.get(key)
|
|
204
222
|
if future:
|
|
205
223
|
await future
|
|
206
|
-
|
|
224
|
+
# Future tamamlandığında sonucu döndür.
|
|
225
|
+
value = future.result()
|
|
207
226
|
# konsol.log(f"[yellow][?] {key}")
|
|
208
|
-
return
|
|
227
|
+
return value
|
|
228
|
+
# Eğer future da yoksa, key bulunamadığına dair hata fırlat.
|
|
229
|
+
raise KeyError(key)
|
|
209
230
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
"""
|
|
214
|
-
Belirtilen key'in süresi dolduysa, cache ve futures içerisinden temizler.
|
|
215
|
-
"""
|
|
216
|
-
if self._ttl is not UNLIMITED and self._is_expired(key):
|
|
217
|
-
# konsol.log(f"[red][-] {key}")
|
|
218
|
-
self._data.pop(key, None)
|
|
219
|
-
self._times.pop(key, None)
|
|
220
|
-
self.futures.pop(key, None)
|
|
221
|
-
|
|
222
|
-
def __setitem__(self, key, value):
|
|
231
|
+
async def set(self, key, value):
|
|
232
|
+
"""Belirtilen key için cache'e değer ekler."""
|
|
233
|
+
self.ensure_cleanup_task()
|
|
223
234
|
self._data[key] = value
|
|
224
235
|
if self._ttl is not UNLIMITED:
|
|
225
|
-
self._times[key] = time()
|
|
236
|
+
self._times[key] = time.time()
|
|
237
|
+
|
|
238
|
+
class HybridAsyncCache:
|
|
239
|
+
"""
|
|
240
|
+
Öncelikle Redis cache kullanılmaya çalışılır.
|
|
241
|
+
Hata durumunda veya Redis erişilemiyorsa in-memory AsyncCache’e geçilir.
|
|
242
|
+
"""
|
|
243
|
+
def __init__(self, ttl=UNLIMITED):
|
|
244
|
+
self._ttl = ttl
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
self.redis = redis.Redis(
|
|
248
|
+
host = "127.0.0.1",
|
|
249
|
+
port = 6379,
|
|
250
|
+
decode_responses = False
|
|
251
|
+
)
|
|
252
|
+
except Exception:
|
|
253
|
+
self.redis = None
|
|
254
|
+
|
|
255
|
+
self.memory = AsyncCache(ttl)
|
|
256
|
+
self.futures = {}
|
|
257
|
+
|
|
258
|
+
async def get(self, key):
|
|
259
|
+
# Eşzamanlı istek yönetimi: aynı key için bir future varsa, direkt bekleyip sonucu döndür.
|
|
260
|
+
if key in self.futures:
|
|
261
|
+
# konsol.log(f"[yellow][?] {key}")
|
|
262
|
+
return await self.futures[key]
|
|
263
|
+
|
|
264
|
+
# İlk önce Redis ile deneyelim
|
|
265
|
+
if self.redis:
|
|
266
|
+
try:
|
|
267
|
+
data = await self.redis.get(key)
|
|
268
|
+
except Exception:
|
|
269
|
+
return await self.memory.get(key)
|
|
270
|
+
if data is not None:
|
|
271
|
+
try:
|
|
272
|
+
result = pickle.loads(data)
|
|
273
|
+
# konsol.log(f"[yellow][~] {key}")
|
|
274
|
+
return result
|
|
275
|
+
except Exception:
|
|
276
|
+
# Deserialize hatası durumunda in-memory cache'ten dene
|
|
277
|
+
return await self.memory.get(key)
|
|
278
|
+
else:
|
|
279
|
+
# Redis'te veri yoksa, in-memory cache'e bak
|
|
280
|
+
return await self.memory.get(key)
|
|
281
|
+
else:
|
|
282
|
+
# Redis kullanılmıyorsa doğrudan in-memory cache'e bak
|
|
283
|
+
return await self.memory.get(key)
|
|
226
284
|
|
|
285
|
+
async def set(self, key, value):
|
|
286
|
+
# Önce veriyi pickle etmeyi deniyoruz.
|
|
287
|
+
try:
|
|
288
|
+
ser = pickle.dumps(value)
|
|
289
|
+
except Exception:
|
|
290
|
+
# Serialization hatası durumunda sadece in-memory cache'e yaz
|
|
291
|
+
await self.memory.set(key, value)
|
|
292
|
+
return
|
|
293
|
+
|
|
294
|
+
if self.redis:
|
|
295
|
+
try:
|
|
296
|
+
if self._ttl is not UNLIMITED:
|
|
297
|
+
await self.redis.set(key, ser, ex=self._ttl)
|
|
298
|
+
else:
|
|
299
|
+
await self.redis.set(key, ser)
|
|
300
|
+
return
|
|
301
|
+
except Exception:
|
|
302
|
+
# Redis yazma hatası durumunda in-memory fallback
|
|
303
|
+
await self.memory.set(key, value)
|
|
304
|
+
return
|
|
305
|
+
else:
|
|
306
|
+
# Redis yoksa in-memory cache'e yaz
|
|
307
|
+
await self.memory.set(key, value)
|
|
227
308
|
|
|
228
309
|
# -----------------------------------------------------
|
|
229
|
-
#
|
|
310
|
+
# Cache'e Hesaplanmış Sonucu Yazma Yardımcı Fonksiyonları
|
|
230
311
|
# -----------------------------------------------------
|
|
231
312
|
|
|
232
313
|
def _sync_maybe_cache(func, key, result, unless):
|
|
233
|
-
"""Senkron
|
|
314
|
+
"""Senkron fonksiyon sonucu için cache kaydı oluşturur."""
|
|
234
315
|
if unless is None or not unless(result):
|
|
235
316
|
func.__cache[key] = result
|
|
236
317
|
# konsol.log(f"[green][+] {key}")
|
|
237
318
|
|
|
238
319
|
async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
|
|
239
320
|
"""
|
|
240
|
-
Asenkron fonksiyon sonucunu hesaplar ve cache'e
|
|
241
|
-
Aynı key için
|
|
242
|
-
Sonuç, unless(result) True değilse cache'e eklenir.
|
|
321
|
+
Asenkron fonksiyon sonucunu hesaplar ve cache'e yazar.
|
|
322
|
+
Aynı key için gelen eşzamanlı çağrılar future üzerinden bekletilir.
|
|
243
323
|
"""
|
|
244
324
|
# __cache'den cache nesnesini alıyoruz.
|
|
245
325
|
cache = func.__cache
|
|
@@ -256,12 +336,12 @@ async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
|
|
|
256
336
|
# Asenkron fonksiyonu çalıştır ve sonucu elde et.
|
|
257
337
|
result = await func(*args, **kwargs)
|
|
258
338
|
future.set_result(result)
|
|
259
|
-
|
|
339
|
+
|
|
260
340
|
# unless koşuluna göre cache'e ekleme yap.
|
|
261
341
|
if unless is None or not unless(result):
|
|
262
|
-
cache
|
|
342
|
+
await cache.set(key, result)
|
|
263
343
|
# konsol.log(f"[green][+] {key}")
|
|
264
|
-
|
|
344
|
+
|
|
265
345
|
return result
|
|
266
346
|
except Exception as exc:
|
|
267
347
|
future.cancel()
|
|
@@ -270,44 +350,35 @@ async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
|
|
|
270
350
|
# İşlem tamamlandığında future'ı temizle.
|
|
271
351
|
cache.futures.pop(key, None)
|
|
272
352
|
|
|
273
|
-
|
|
274
353
|
# -----------------------------------------------------
|
|
275
|
-
#
|
|
354
|
+
# kekik_cache Dekoratörü (Senkrondan Asenkrona)
|
|
276
355
|
# -----------------------------------------------------
|
|
277
356
|
|
|
278
|
-
def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False):
|
|
357
|
+
def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False, use_redis=True):
|
|
279
358
|
"""
|
|
280
|
-
Bir
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
is_fastapi (bool, optional): Eğer `True` ise, cache key'i oluştururken FastAPI request nesnesine özel şekilde davranır.
|
|
288
|
-
Varsayılan olarak `False`'tır.
|
|
289
|
-
|
|
290
|
-
Notlar:
|
|
291
|
-
-------
|
|
292
|
-
Normalde yalnızca Redis cache kullanılmak istenir.
|
|
293
|
-
Ancak Redis'e ulaşılamayan durumlarda RAM fallback kullanılır.
|
|
294
|
-
|
|
295
|
-
---
|
|
296
|
-
|
|
297
|
-
Örn.:
|
|
359
|
+
Bir fonksiyonun (senkron/asenkron) sonucunu cache'ler.
|
|
360
|
+
|
|
361
|
+
Parametreler:
|
|
362
|
+
- ttl: Cache'in geçerlilik süresi (saniye). UNLIMITED ise süresizdir.
|
|
363
|
+
- unless: Sonuç alınmadan önce çağrılan, True dönerse cache'e alınmaz.
|
|
364
|
+
- is_fastapi: True ise, FastAPI Request nesnesine göre key oluşturur.
|
|
365
|
+
- use_redis: Asenkron fonksiyonlarda Redis kullanımı (Hybrid cache) için True verilebilir.
|
|
298
366
|
|
|
299
|
-
|
|
367
|
+
Örnek Kullanım:
|
|
368
|
+
|
|
369
|
+
@kekik_cache(ttl=15, unless=lambda sonuc: sonuc is None)
|
|
300
370
|
async def bakalim(param):
|
|
301
|
-
# Burada cache işlemi yapılır.
|
|
302
371
|
return param
|
|
303
372
|
"""
|
|
304
|
-
# Parametresiz
|
|
373
|
+
# Parametresiz kullanım durumunda
|
|
305
374
|
if callable(ttl):
|
|
306
|
-
return kekik_cache(UNLIMITED, unless=unless, is_fastapi=is_fastapi)(ttl)
|
|
375
|
+
return kekik_cache(UNLIMITED, unless=unless, is_fastapi=is_fastapi, use_redis=use_redis)(ttl)
|
|
307
376
|
|
|
308
377
|
def decorator(func):
|
|
309
378
|
if asyncio.iscoroutinefunction(func):
|
|
310
|
-
|
|
379
|
+
# Asenkron fonksiyonlar için cache türünü seçelim:
|
|
380
|
+
|
|
381
|
+
func.__cache = HybridAsyncCache(ttl) if use_redis else AsyncCache(ttl)
|
|
311
382
|
|
|
312
383
|
@wraps(func)
|
|
313
384
|
async def async_wrapper(*args, **kwargs):
|
|
@@ -320,6 +391,7 @@ def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False):
|
|
|
320
391
|
|
|
321
392
|
return async_wrapper
|
|
322
393
|
else:
|
|
394
|
+
# Senkron fonksiyonlar için
|
|
323
395
|
func.__cache = SyncCache(ttl)
|
|
324
396
|
|
|
325
397
|
@wraps(func)
|
|
@@ -6,7 +6,7 @@ from io import open
|
|
|
6
6
|
setup(
|
|
7
7
|
# ? Genel Bilgiler
|
|
8
8
|
name = "Kekik",
|
|
9
|
-
version = "1.7.
|
|
9
|
+
version = "1.7.2",
|
|
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
|