Kekik 1.6.1__py3-none-any.whl → 1.6.2__py3-none-any.whl
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/cache.py +164 -0
- {Kekik-1.6.1.dist-info → Kekik-1.6.2.dist-info}/METADATA +13 -2
- {Kekik-1.6.1.dist-info → Kekik-1.6.2.dist-info}/RECORD +7 -6
- {Kekik-1.6.1.dist-info → Kekik-1.6.2.dist-info}/WHEEL +1 -1
- {Kekik-1.6.1.dist-info → Kekik-1.6.2.dist-info}/LICENSE +0 -0
- {Kekik-1.6.1.dist-info → Kekik-1.6.2.dist-info}/entry_points.txt +0 -0
- {Kekik-1.6.1.dist-info → Kekik-1.6.2.dist-info}/top_level.txt +0 -0
Kekik/cache.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# ! https://github.com/Fatal1ty/aiocached
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from functools import wraps
|
|
5
|
+
from time import time
|
|
6
|
+
|
|
7
|
+
UNLIMITED = None
|
|
8
|
+
|
|
9
|
+
class Cache:
|
|
10
|
+
"""
|
|
11
|
+
Basit in-memory cache yapısı.
|
|
12
|
+
TTL (time-to-live) süresi dolan veriler otomatik olarak temizlenir.
|
|
13
|
+
"""
|
|
14
|
+
def __init__(self, ttl=UNLIMITED):
|
|
15
|
+
self._data = {}
|
|
16
|
+
self._ttl = ttl
|
|
17
|
+
self._times = {}
|
|
18
|
+
|
|
19
|
+
def _is_expired(self, key):
|
|
20
|
+
"""Belirtilen key'in süresi dolduysa True döner."""
|
|
21
|
+
if self._ttl is UNLIMITED:
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
timestamp = self._times.get(key)
|
|
25
|
+
|
|
26
|
+
return timestamp is not None and (time() - timestamp > self._ttl)
|
|
27
|
+
|
|
28
|
+
def remove_if_expired(self, key):
|
|
29
|
+
"""
|
|
30
|
+
Eğer key'in cache süresi dolmuşsa, ilgili entry'yi temizler.
|
|
31
|
+
"""
|
|
32
|
+
if self._is_expired(key):
|
|
33
|
+
self._data.pop(key, None)
|
|
34
|
+
self._times.pop(key, None)
|
|
35
|
+
|
|
36
|
+
def __getitem__(self, key):
|
|
37
|
+
self.remove_if_expired(key)
|
|
38
|
+
return self._data[key]
|
|
39
|
+
|
|
40
|
+
def __setitem__(self, key, value):
|
|
41
|
+
self._data[key] = value
|
|
42
|
+
if self._ttl is not UNLIMITED:
|
|
43
|
+
self._times[key] = time()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class AsyncCache(Cache):
|
|
47
|
+
"""
|
|
48
|
+
Asenkron işlemleri destekleyen cache yapısı.
|
|
49
|
+
Aynı key için gelen eşzamanlı çağrılar, futures kullanılarak tek sonuç üzerinden paylaşılır.
|
|
50
|
+
"""
|
|
51
|
+
def __init__(self, ttl=UNLIMITED):
|
|
52
|
+
super().__init__(ttl)
|
|
53
|
+
self.futures = {}
|
|
54
|
+
|
|
55
|
+
async def get(self, key):
|
|
56
|
+
"""
|
|
57
|
+
Belirtilen key için cache'de saklanan değeri asenkron olarak döndürür.
|
|
58
|
+
Eğer key bulunamazsa, ilgili future üzerinden beklemeyi sağlar.
|
|
59
|
+
"""
|
|
60
|
+
self.remove_if_expired(key)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
return self._data[key]
|
|
64
|
+
except KeyError as e:
|
|
65
|
+
future = self.futures.get(key)
|
|
66
|
+
if future:
|
|
67
|
+
await future
|
|
68
|
+
return future.result()
|
|
69
|
+
|
|
70
|
+
raise e
|
|
71
|
+
|
|
72
|
+
def remove_if_expired(self, key):
|
|
73
|
+
"""
|
|
74
|
+
Belirtilen key'in süresi dolduysa, cache ve futures içerisinden temizler.
|
|
75
|
+
"""
|
|
76
|
+
if self._ttl is not UNLIMITED and self._is_expired(key):
|
|
77
|
+
self._data.pop(key, None)
|
|
78
|
+
self._times.pop(key, None)
|
|
79
|
+
self.futures.pop(key, None)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _sync_maybe_cache(func, key, result, unless):
|
|
83
|
+
"""Senkron sonuç için cache kaydını oluşturur (unless koşuluna bakarak)."""
|
|
84
|
+
if unless is None or not unless(result):
|
|
85
|
+
func.__cache[key] = result
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
|
|
89
|
+
"""
|
|
90
|
+
Asenkron fonksiyon sonucu hesaplandıktan sonra, sonucu cache’e ekler.
|
|
91
|
+
Eğer `unless` koşulu sağlanıyorsa cache kaydı atlanır.
|
|
92
|
+
"""
|
|
93
|
+
cache = func.__cache
|
|
94
|
+
future = asyncio.Future()
|
|
95
|
+
cache.futures[key] = future
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
result = await func(*args, **kwargs)
|
|
99
|
+
except Exception as exc:
|
|
100
|
+
cache.futures.pop(key, None)
|
|
101
|
+
future.cancel()
|
|
102
|
+
raise exc
|
|
103
|
+
|
|
104
|
+
future.set_result(result)
|
|
105
|
+
|
|
106
|
+
if unless is None or not unless(result):
|
|
107
|
+
cache[key] = result
|
|
108
|
+
else:
|
|
109
|
+
cache.futures.pop(key, None)
|
|
110
|
+
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
def make_cache_key(args, kwargs):
|
|
114
|
+
"""Fonksiyon argümanlarından cache için kullanılacak key’i oluşturur."""
|
|
115
|
+
return (args, tuple(sorted(kwargs.items())))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def kekik_cache(ttl=UNLIMITED, unless=None):
|
|
119
|
+
"""
|
|
120
|
+
Bir fonksiyon veya coroutine'in sonucunu cache'ler.
|
|
121
|
+
|
|
122
|
+
:param ttl: Cache’in geçerlilik süresi (saniye). UNLIMITED ise süresizdir.
|
|
123
|
+
:param unless: Fonksiyonun sonucunu argüman olarak alan bir callable.
|
|
124
|
+
Eğer True dönerse, sonuç cache'e alınmaz.
|
|
125
|
+
|
|
126
|
+
Örnek kullanım:
|
|
127
|
+
|
|
128
|
+
@kekik_cache(ttl=15, unless=lambda res: res is None)
|
|
129
|
+
async def my_func(param):
|
|
130
|
+
...
|
|
131
|
+
"""
|
|
132
|
+
# Parametresiz kullanıldığında
|
|
133
|
+
if callable(ttl):
|
|
134
|
+
return kekik_cache(UNLIMITED, unless=unless)(ttl)
|
|
135
|
+
|
|
136
|
+
def decorator(func):
|
|
137
|
+
func.__cache = AsyncCache(ttl)
|
|
138
|
+
|
|
139
|
+
if asyncio.iscoroutinefunction(func):
|
|
140
|
+
@wraps(func)
|
|
141
|
+
async def async_wrapper(*args, **kwargs):
|
|
142
|
+
key = make_cache_key(args, kwargs)
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
return await func.__cache.get(key)
|
|
146
|
+
except KeyError:
|
|
147
|
+
return await _async_compute_and_cache(func, key, unless, *args, **kwargs)
|
|
148
|
+
|
|
149
|
+
return async_wrapper
|
|
150
|
+
else:
|
|
151
|
+
@wraps(func)
|
|
152
|
+
def sync_wrapper(*args, **kwargs):
|
|
153
|
+
key = make_cache_key(args, kwargs)
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
return func.__cache[key]
|
|
157
|
+
except KeyError:
|
|
158
|
+
result = func(*args, **kwargs)
|
|
159
|
+
_sync_maybe_cache(func, key, result, unless)
|
|
160
|
+
return result
|
|
161
|
+
|
|
162
|
+
return sync_wrapper
|
|
163
|
+
|
|
164
|
+
return decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: Kekik
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.2
|
|
4
4
|
Summary: İşlerimizi kolaylaştıracak fonksiyonların el altında durduğu kütüphane..
|
|
5
5
|
Home-page: https://github.com/keyiflerolsun/Kekik
|
|
6
6
|
Author: keyiflerolsun
|
|
@@ -26,6 +26,17 @@ Requires-Dist: simplejson
|
|
|
26
26
|
Requires-Dist: rich
|
|
27
27
|
Requires-Dist: tabulate
|
|
28
28
|
Requires-Dist: pycryptodome
|
|
29
|
+
Dynamic: author
|
|
30
|
+
Dynamic: author-email
|
|
31
|
+
Dynamic: classifier
|
|
32
|
+
Dynamic: description
|
|
33
|
+
Dynamic: description-content-type
|
|
34
|
+
Dynamic: home-page
|
|
35
|
+
Dynamic: keywords
|
|
36
|
+
Dynamic: license
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
Dynamic: summary
|
|
29
40
|
|
|
30
41
|
# <img src="https://www.akashtrehan.com/assets/images/emoji/terminal.png" height="42" align="center"> Kekik
|
|
31
42
|
|
|
@@ -2,6 +2,7 @@ Kekik/BIST.py,sha256=gFvEFdLGgsxUq33AkqOr5pVGsSm9jR0v6DVwoSzhX7w,1375
|
|
|
2
2
|
Kekik/Domain2IP.py,sha256=dMDatPElTMfLORzC_QN9znex8Se1eid3EPsxAe5S1aQ,4851
|
|
3
3
|
Kekik/Nesne.py,sha256=33pp49cZkRCoa0aUFfzVMpx5FSJchSdiKhm97Nkol1Q,7020
|
|
4
4
|
Kekik/__init__.py,sha256=Iy-O4c_DuY2ttQJv6j7xoVk3IS9EcC0Aqx3vhFvCgd8,1057
|
|
5
|
+
Kekik/cache.py,sha256=n15Ltoh068iFPK3ANDLz11eisOQXHLwUQA2eB3hjiiY,4861
|
|
5
6
|
Kekik/cli.py,sha256=I--MV8-okBSUYAaV4fRsMeB2n57OJITrgbEnK1OdTPY,3648
|
|
6
7
|
Kekik/csv2dict.py,sha256=HO0AgREXE0yM7cL4OwqpkCGm2pz31RTb0mhnTg6gdwo,423
|
|
7
8
|
Kekik/dict2csv.py,sha256=AcGvEg9i5MXtwMwg7WiDxOj8h9LldNjjcRiWFwA9XJU,560
|
|
@@ -33,9 +34,9 @@ Kekik/kisi_ver/__init__.py,sha256=gH613YZC3ziE7f67dViefRSuwDwsHyVvXHpM5CzZ2q4,13
|
|
|
33
34
|
Kekik/kisi_ver/biyografiler.py,sha256=5Xv1ixaDGHGtl5Nf92jo9dPgF3jDXOGEPmWgoEsn9j8,189486
|
|
34
35
|
Kekik/kisi_ver/isimler.py,sha256=zHVimWL_4TvoLE3qzWQslDBc8-IJZSB02s0vRwsVM1g,88066
|
|
35
36
|
Kekik/kisi_ver/soyisimler.py,sha256=YQJYp2SjENgwOaCa9mmShxPYeeUll2cq8Vox-d8_kB8,78485
|
|
36
|
-
Kekik-1.6.
|
|
37
|
-
Kekik-1.6.
|
|
38
|
-
Kekik-1.6.
|
|
39
|
-
Kekik-1.6.
|
|
40
|
-
Kekik-1.6.
|
|
41
|
-
Kekik-1.6.
|
|
37
|
+
Kekik-1.6.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
38
|
+
Kekik-1.6.2.dist-info/METADATA,sha256=6ZwkPdUXWuo-lBZTi2r0EyGfALiayeDYAeaJMVEu8bk,43959
|
|
39
|
+
Kekik-1.6.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
40
|
+
Kekik-1.6.2.dist-info/entry_points.txt,sha256=yjBifxtRlqfg8lPkH4Bu-urSa5ecptCHsuth-DcyWcg,59
|
|
41
|
+
Kekik-1.6.2.dist-info/top_level.txt,sha256=NotddscfgxawvuRyAa7xkgnMhyteFDcBxb5aU5GY3BM,6
|
|
42
|
+
Kekik-1.6.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|