Kekik 1.7.1__py3-none-any.whl → 1.7.3__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 CHANGED
@@ -1,22 +1,32 @@
1
- # ! https://github.com/Fatal1ty/aiocached
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 asyncio, threading
7
+ import time
8
+ import threading
9
+ import asyncio
10
+ import pickle
11
+
12
+ # Redis client (Redis yoksa fallback yapılacak)
13
+ import redis.asyncio as redisAsync
14
+ import redis as redis
9
15
 
10
16
  # -----------------------------------------------------
11
- # Yardımcı Fonksiyonlar
17
+ # Sabitler ve Yardımcı Fonksiyonlar
12
18
  # -----------------------------------------------------
19
+ UNLIMITED = None
13
20
 
14
- UNLIMITED = None
21
+ REDIS_HOST = "127.0.0.1"
22
+ REDIS_PORT = 6379
23
+ REDIS_DB = 0
24
+ REDIS_PASS = None
15
25
 
16
26
  def normalize_for_key(value):
17
27
  """
18
28
  Cache key oluşturma amacıyla verilen değeri normalize eder.
19
- - Basit tipler (int, float, str, bool, None) direk kullanılır.
29
+ - Basit tipler (int, float, str, bool, None) doğrudan kullanılır.
20
30
  - dict: Anahtarları sıralı olarak normalize eder.
21
31
  - list/tuple: Elemanları normalize eder.
22
32
  - Diğer: Sadece sınıf ismi kullanılır.
@@ -36,7 +46,7 @@ def normalize_for_key(value):
36
46
  def simple_cache_key(func, args, kwargs) -> str:
37
47
  """
38
48
  Fonksiyonun tam adı ve parametrelerini kullanarak bir cache key oluşturur.
39
- Oluşturulan stringin sonuna MD5 hash eklenir.
49
+ Oluşturulan stringin sonuna MD5 hash eklenebilir.
40
50
  """
41
51
  base_key = f"{func.__module__}.{func.__qualname__}"
42
52
 
@@ -60,7 +70,7 @@ async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
60
70
  if not is_fastapi:
61
71
  return simple_cache_key(func, args, kwargs)
62
72
 
63
- # FastAPI: request ilk argüman ya da kwargs'dan alınır
73
+ # FastAPI: request ilk argümandan ya da kwargs'dan alınır.
64
74
  request = args[0] if args else kwargs.get("request")
65
75
 
66
76
  if request.method == "GET":
@@ -73,56 +83,52 @@ async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
73
83
  form_data = await request.form()
74
84
  veri = dict(form_data.items())
75
85
 
86
+ # Eğer "kurek" gibi özel parametreler varsa temizleyebilirsiniz:
87
+ veri.pop("kurek", None)
88
+
76
89
  args_hash = md5(urlencode(veri).encode()).hexdigest() if veri else ""
77
90
  return f"{request.url.path}?{veri}"
78
91
 
79
-
80
92
  # -----------------------------------------------------
81
- # In-Memory (RAM) Cache
93
+ # Senkron Cache (RAM) Sınıfı
82
94
  # -----------------------------------------------------
83
95
 
84
- class Cache:
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):
96
+ class SyncCache:
101
97
  """
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.
98
+ Senkron fonksiyonlar için basit in-memory cache.
99
+ TTL süresi dolan veriler periyodik olarak arka plan thread’iyle temizlenir.
105
100
  """
106
101
  def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60):
107
- super().__init__(ttl)
102
+ self._ttl = ttl
103
+ self._data = {}
104
+ self._times = {}
108
105
 
109
106
  # TTL sınırsız değilse, cleanup_interval ile ttl'den büyük olanı kullanıyoruz.
110
107
  self._cleanup_interval = max(ttl, cleanup_interval) if ttl is not UNLIMITED else cleanup_interval
111
- self._lock = threading.RLock()
112
108
 
113
109
  # Arka planda çalışan ve periyodik olarak expired entry'leri temizleyen thread başlatılıyor.
110
+ self._lock = threading.RLock()
114
111
  self._cleanup_thread = threading.Thread(target=self._auto_cleanup, daemon=True)
115
112
  self._cleanup_thread.start()
116
113
 
117
114
  def _auto_cleanup(self):
118
115
  """Belirlenen aralıklarla cache içerisindeki süresi dolmuş entry'leri temizler."""
119
116
  while True:
120
- sleep(self._cleanup_interval)
117
+ time.sleep(self._cleanup_interval)
121
118
  with self._lock:
122
119
  keys = list(self._data.keys())
123
120
  for key in keys:
124
121
  self.remove_if_expired(key)
125
122
 
123
+ def _is_expired(self, key):
124
+ """Belirtilen key'in süresi dolmuşsa True döner."""
125
+ if self._ttl is UNLIMITED:
126
+ return False
127
+
128
+ timestamp = self._times.get(key)
129
+
130
+ return timestamp is not None and (time.time() - timestamp > self._ttl)
131
+
126
132
  def remove_if_expired(self, key):
127
133
  """
128
134
  Eğer key'in cache süresi dolmuşsa, ilgili entry'yi temizler.
@@ -144,26 +150,104 @@ class SyncCache(Cache):
144
150
  def __setitem__(self, key, value):
145
151
  with self._lock:
146
152
  self._data[key] = value
147
- # konsol.log(f"[green][+] {key}")
148
153
  if self._ttl is not UNLIMITED:
149
- self._times[key] = time()
154
+ self._times[key] = time.time()
155
+
156
+ class HybridSyncCache:
157
+ """
158
+ Senkron işlemler için, öncelikle Redis cache kullanılmaya çalışılır.
159
+ Redis'ten veri alınamazsa ya da hata oluşursa, SyncCache (in-memory) fallback uygulanır.
160
+ """
161
+ def __init__(self, ttl=None):
162
+ self._ttl = ttl
163
+
164
+ try:
165
+ self.redis = redis.Redis(
166
+ host = REDIS_HOST,
167
+ port = REDIS_PORT,
168
+ db = REDIS_DB,
169
+ password = REDIS_PASS,
170
+ decode_responses = False
171
+ )
172
+ self.redis.ping()
173
+ except Exception:
174
+ self.redis = None
175
+
176
+ self.memory = SyncCache(ttl)
177
+
178
+ def get(self, key):
179
+ # Önce Redis ile deniyoruz:
180
+ if self.redis:
181
+ try:
182
+ data = self.redis.get(key)
183
+ except Exception:
184
+ data = None
185
+ if data is not None:
186
+ try:
187
+ result = pickle.loads(data)
188
+ # konsol.log(f"[yellow][~] {key}")
189
+ return result
190
+ except Exception:
191
+ # Deserialize hatası durumunda fallback'e geç
192
+ pass
150
193
 
194
+ # Redis'te veri yoksa, yerel cache'ten alıyoruz.
195
+ try:
196
+ return self.memory[key]
197
+ except KeyError:
198
+ raise KeyError(key)
199
+
200
+ def set(self, key, value):
201
+ try:
202
+ ser = pickle.dumps(value)
203
+ except Exception:
204
+ # Serialization hatası durumunda yerel cache'e yazalım.
205
+ self.memory[key] = value
206
+ return
207
+
208
+ if self.redis:
209
+ try:
210
+ if self._ttl is not None:
211
+ self.redis.set(key, ser, ex=self._ttl)
212
+ else:
213
+ self.redis.set(key, ser)
214
+ return
215
+ except Exception:
216
+ # Redis'e yazılamazsa yerel cache'e geçelim.
217
+ self.memory[key] = value
218
+ return
219
+ else:
220
+ # Redis kullanılmıyorsa direkt yerel cache'e yaz.
221
+ self.memory[key] = value
222
+
223
+ # HybridSyncCache'in subscriptable olması için:
224
+ def __getitem__(self, key):
225
+ return self.get(key)
226
+
227
+ def __setitem__(self, key, value):
228
+ self.set(key, value)
229
+
230
+ # -----------------------------------------------------
231
+ # Asenkron Cache (In-Memory) ve Redis Hybrid Cache
232
+ # -----------------------------------------------------
151
233
 
152
- class AsyncCache(Cache):
234
+ class AsyncCache:
153
235
  """
154
- Asenkron işlemleri destekleyen cache yapısı.
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.
236
+ Temel in-memory asenkron cache.
157
237
  """
158
238
  def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60):
159
239
  """
160
240
  :param ttl: Her entry için geçerli süre (saniye). Örneğin 3600 saniye 1 saattir.
161
241
  :param cleanup_interval: Otomatik temizleme görevinin kaç saniyede bir çalışacağını belirler.
162
242
  """
163
- super().__init__(ttl)
243
+ self._ttl = ttl
244
+ self._data = {}
245
+ self._times = {}
164
246
  self.futures = {}
165
247
 
248
+ # TTL sınırsız değilse, cleanup_interval ile ttl'den büyük olanı kullanıyoruz.
166
249
  self._cleanup_interval = max(ttl, cleanup_interval) if ttl is not UNLIMITED else cleanup_interval
250
+
167
251
  # Aktif bir event loop varsa otomatik temizlik görevini başlatıyoruz.
168
252
  try:
169
253
  self._cleanup_task = asyncio.get_running_loop().create_task(self._auto_cleanup())
@@ -174,9 +258,7 @@ class AsyncCache(Cache):
174
258
  """Belirlenen aralıklarla cache içerisindeki süresi dolmuş entry'leri temizler."""
175
259
  while True:
176
260
  await asyncio.sleep(self._cleanup_interval)
177
- # _data kopyasını almak, üzerinde dönüp silme yaparken hata almamak için.
178
- keys = list(self._data.keys())
179
- for key in keys:
261
+ for key in list(self._data.keys()):
180
262
  self.remove_if_expired(key)
181
263
 
182
264
  def ensure_cleanup_task(self):
@@ -187,59 +269,139 @@ class AsyncCache(Cache):
187
269
  except RuntimeError:
188
270
  pass # Yine loop yoksa yapacak bir şey yok
189
271
 
272
+ def remove_if_expired(self, key):
273
+ """
274
+ Belirtilen key'in süresi dolduysa, cache ve futures içerisinden temizler.
275
+ """
276
+ if self._ttl is not UNLIMITED:
277
+ t = self._times.get(key)
278
+ if t is not None and (time.time() - t > self._ttl):
279
+ # konsol.log(f"[red][-] {key}")
280
+ self._data.pop(key, None)
281
+ self._times.pop(key, None)
282
+ self.futures.pop(key, None)
283
+
190
284
  async def get(self, key):
191
285
  """
192
286
  Belirtilen key için cache'de saklanan değeri asenkron olarak döndürür.
193
287
  Eğer key bulunamazsa, ilgili future üzerinden beklemeyi sağlar.
194
288
  """
289
+ # Cleanup task'in aktif olduğundan emin olun.
195
290
  self.ensure_cleanup_task()
291
+ # Eğer key'in süresi dolmuşsa, kaldırın.
196
292
  self.remove_if_expired(key)
197
293
 
198
294
  try:
199
- veri = self._data[key]
295
+ # Cache içerisinde key varsa direkt değeri döndür.
296
+ value = self._data[key]
200
297
  # konsol.log(f"[yellow][~] {key}")
201
- return veri
202
- except KeyError as e:
298
+ return value
299
+ except KeyError:
300
+ # Eğer key cache'de yoksa, aynı key ile başlatılmış future varsa onu bekle.
203
301
  future = self.futures.get(key)
204
302
  if future:
205
303
  await future
206
- veri = future.result()
304
+ # Future tamamlandığında sonucu döndür.
305
+ value = future.result()
207
306
  # konsol.log(f"[yellow][?] {key}")
208
- return veri
307
+ return value
308
+ # Eğer future da yoksa, key bulunamadığına dair hata fırlat.
309
+ raise KeyError(key)
209
310
 
210
- raise e
211
-
212
- def remove_if_expired(self, key):
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):
311
+ async def set(self, key, value):
312
+ """Belirtilen key için cache'e değer ekler."""
313
+ self.ensure_cleanup_task()
223
314
  self._data[key] = value
224
315
  if self._ttl is not UNLIMITED:
225
- self._times[key] = time()
316
+ self._times[key] = time.time()
226
317
 
318
+ class HybridAsyncCache:
319
+ """
320
+ Öncelikle Redis cache kullanılmaya çalışılır.
321
+ Hata durumunda veya Redis erişilemiyorsa in-memory AsyncCache’e geçilir.
322
+ """
323
+ def __init__(self, ttl=UNLIMITED):
324
+ self._ttl = ttl
325
+
326
+ try:
327
+ self.redis = redisAsync.Redis(
328
+ host = REDIS_HOST,
329
+ port = REDIS_PORT,
330
+ db = REDIS_DB,
331
+ password = REDIS_PASS,
332
+ decode_responses = False
333
+ )
334
+ except Exception:
335
+ self.redis = None
336
+
337
+ self.memory = AsyncCache(ttl)
338
+ self.futures = {}
339
+
340
+ async def get(self, key):
341
+ # Eşzamanlı istek yönetimi: aynı key için bir future varsa, direkt bekleyip sonucu döndür.
342
+ if key in self.futures:
343
+ # konsol.log(f"[yellow][?] {key}")
344
+ return await self.futures[key]
345
+
346
+ # İlk önce Redis ile deneyelim
347
+ if self.redis:
348
+ try:
349
+ data = await self.redis.get(key)
350
+ except Exception:
351
+ return await self.memory.get(key)
352
+ if data is not None:
353
+ try:
354
+ result = pickle.loads(data)
355
+ # konsol.log(f"[yellow][~] {key}")
356
+ return result
357
+ except Exception:
358
+ # Deserialize hatası durumunda in-memory cache'ten dene
359
+ return await self.memory.get(key)
360
+ else:
361
+ # Redis'te veri yoksa, in-memory cache'e bak
362
+ return await self.memory.get(key)
363
+ else:
364
+ # Redis kullanılmıyorsa doğrudan in-memory cache'e bak
365
+ return await self.memory.get(key)
366
+
367
+ async def set(self, key, value):
368
+ # Önce veriyi pickle etmeyi deniyoruz.
369
+ try:
370
+ ser = pickle.dumps(value)
371
+ except Exception:
372
+ # Serialization hatası durumunda sadece in-memory cache'e yaz
373
+ await self.memory.set(key, value)
374
+ return
375
+
376
+ if self.redis:
377
+ try:
378
+ if self._ttl is not UNLIMITED:
379
+ await self.redis.set(key, ser, ex=self._ttl)
380
+ else:
381
+ await self.redis.set(key, ser)
382
+ return
383
+ except Exception:
384
+ # Redis yazma hatası durumunda in-memory fallback
385
+ await self.memory.set(key, value)
386
+ return
387
+ else:
388
+ # Redis yoksa in-memory cache'e yaz
389
+ await self.memory.set(key, value)
227
390
 
228
391
  # -----------------------------------------------------
229
- # Fonksiyonun Sonucunu Hesaplayıp Cache'e Yazma
392
+ # Cache'e Hesaplanmış Sonucu Yazma Yardımcı Fonksiyonları
230
393
  # -----------------------------------------------------
231
394
 
232
395
  def _sync_maybe_cache(func, key, result, unless):
233
- """Senkron sonuç için cache kaydını oluşturur (unless koşuluna bakarak)."""
396
+ """Senkron fonksiyon sonucu için cache kaydı oluşturur."""
234
397
  if unless is None or not unless(result):
235
398
  func.__cache[key] = result
236
399
  # konsol.log(f"[green][+] {key}")
237
400
 
238
401
  async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
239
402
  """
240
- Asenkron fonksiyon sonucunu hesaplar ve cache'e ekler.
241
- Aynı key için işlem devam ediyorsa, mevcut sonucu bekler.
242
- Sonuç, unless(result) True değilse cache'e eklenir.
403
+ Asenkron fonksiyon sonucunu hesaplar ve cache'e yazar.
404
+ Aynı key için gelen eşzamanlı çağrılar future üzerinden bekletilir.
243
405
  """
244
406
  # __cache'den cache nesnesini alıyoruz.
245
407
  cache = func.__cache
@@ -256,12 +418,12 @@ async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
256
418
  # Asenkron fonksiyonu çalıştır ve sonucu elde et.
257
419
  result = await func(*args, **kwargs)
258
420
  future.set_result(result)
259
-
421
+
260
422
  # unless koşuluna göre cache'e ekleme yap.
261
423
  if unless is None or not unless(result):
262
- cache[key] = result
424
+ await cache.set(key, result)
263
425
  # konsol.log(f"[green][+] {key}")
264
-
426
+
265
427
  return result
266
428
  except Exception as exc:
267
429
  future.cancel()
@@ -270,44 +432,35 @@ async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
270
432
  # İşlem tamamlandığında future'ı temizle.
271
433
  cache.futures.pop(key, None)
272
434
 
273
-
274
435
  # -----------------------------------------------------
275
- # Dekoratör: kekik_cache
436
+ # kekik_cache Dekoratörü (Senkrondan Asenkrona)
276
437
  # -----------------------------------------------------
277
438
 
278
- def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False):
439
+ def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False, use_redis=True):
279
440
  """
280
- Bir fonksiyon veya coroutine'in sonucunu cache'ler.
281
-
282
- Args:
283
- ttl (int, optional): Cache’in geçerlilik süresi (saniye).
284
- Eğer `UNLIMITED` ise süresizdir. Varsayılan olarak `UNLIMITED`'dir.
285
- unless (callable, optional): Fonksiyonun sonucunu argüman olarak alan bir callable.
286
- Eğer `True` dönerse, sonuç cache'e alınmaz. Varsayılan olarak `None`'dır.
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.:
441
+ Bir fonksiyonun (senkron/asenkron) sonucunu cache'ler.
298
442
 
299
- @kekik_cache(ttl=15, unless=lambda sonuc: bool(sonuc is None))
443
+ Parametreler:
444
+ - ttl: Cache'in geçerlilik süresi (saniye). UNLIMITED ise süresizdir.
445
+ - unless: Sonuç alınmadan önce çağrılan, True dönerse cache'e alınmaz.
446
+ - is_fastapi: True ise, FastAPI Request nesnesine göre key oluşturur.
447
+ - use_redis: Asenkron fonksiyonlarda Redis kullanımı (Hybrid cache) için True verilebilir.
448
+
449
+ Örnek Kullanım:
450
+
451
+ @kekik_cache(ttl=15, unless=lambda sonuc: sonuc is None)
300
452
  async def bakalim(param):
301
- # Burada cache işlemi yapılır.
302
453
  return param
303
454
  """
304
- # Parametresiz kullanıldığında
455
+ # Parametresiz kullanım durumunda
305
456
  if callable(ttl):
306
- return kekik_cache(UNLIMITED, unless=unless, is_fastapi=is_fastapi)(ttl)
457
+ return kekik_cache(UNLIMITED, unless=unless, is_fastapi=is_fastapi, use_redis=use_redis)(ttl)
307
458
 
308
459
  def decorator(func):
309
460
  if asyncio.iscoroutinefunction(func):
310
- func.__cache = AsyncCache(ttl)
461
+ # Asenkron fonksiyonlar için cache türünü seçelim:
462
+
463
+ func.__cache = HybridAsyncCache(ttl) if use_redis else AsyncCache(ttl)
311
464
 
312
465
  @wraps(func)
313
466
  async def async_wrapper(*args, **kwargs):
@@ -320,7 +473,8 @@ def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False):
320
473
 
321
474
  return async_wrapper
322
475
  else:
323
- func.__cache = SyncCache(ttl)
476
+ # Senkron fonksiyonlar için
477
+ func.__cache = HybridSyncCache(ttl) if use_redis else SyncCache(ttl)
324
478
 
325
479
  @wraps(func)
326
480
  def sync_wrapper(*args, **kwargs):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: Kekik
3
- Version: 1.7.1
3
+ Version: 1.7.3
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
@@ -2,7 +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=Co0r6Jv8sERFP2ulSb8ccoXyFwqCGYv2QNLyqgOWjFM,12092
5
+ Kekik/cache.py,sha256=YJ8hjZY37skYnU-yfPMbLPsEmsokWMzhJk99qkvmYsA,17434
6
6
  Kekik/cli.py,sha256=I--MV8-okBSUYAaV4fRsMeB2n57OJITrgbEnK1OdTPY,3648
7
7
  Kekik/csv2dict.py,sha256=HO0AgREXE0yM7cL4OwqpkCGm2pz31RTb0mhnTg6gdwo,423
8
8
  Kekik/dict2csv.py,sha256=AcGvEg9i5MXtwMwg7WiDxOj8h9LldNjjcRiWFwA9XJU,560
@@ -34,9 +34,9 @@ Kekik/kisi_ver/__init__.py,sha256=gH613YZC3ziE7f67dViefRSuwDwsHyVvXHpM5CzZ2q4,13
34
34
  Kekik/kisi_ver/biyografiler.py,sha256=5Xv1ixaDGHGtl5Nf92jo9dPgF3jDXOGEPmWgoEsn9j8,189486
35
35
  Kekik/kisi_ver/isimler.py,sha256=zHVimWL_4TvoLE3qzWQslDBc8-IJZSB02s0vRwsVM1g,88066
36
36
  Kekik/kisi_ver/soyisimler.py,sha256=YQJYp2SjENgwOaCa9mmShxPYeeUll2cq8Vox-d8_kB8,78485
37
- Kekik-1.7.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
38
- Kekik-1.7.1.dist-info/METADATA,sha256=sTy45X5EuJKCfn9R7Yj4lHdGQdOqLponeeVIAWi7pWE,43959
39
- Kekik-1.7.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
40
- Kekik-1.7.1.dist-info/entry_points.txt,sha256=yjBifxtRlqfg8lPkH4Bu-urSa5ecptCHsuth-DcyWcg,59
41
- Kekik-1.7.1.dist-info/top_level.txt,sha256=NotddscfgxawvuRyAa7xkgnMhyteFDcBxb5aU5GY3BM,6
42
- Kekik-1.7.1.dist-info/RECORD,,
37
+ Kekik-1.7.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
38
+ Kekik-1.7.3.dist-info/METADATA,sha256=cSf0jClm2kfxcF2XJFOxvm_Qk6_nrcgGcqXuIOotRK8,43959
39
+ Kekik-1.7.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
40
+ Kekik-1.7.3.dist-info/entry_points.txt,sha256=yjBifxtRlqfg8lPkH4Bu-urSa5ecptCHsuth-DcyWcg,59
41
+ Kekik-1.7.3.dist-info/top_level.txt,sha256=NotddscfgxawvuRyAa7xkgnMhyteFDcBxb5aU5GY3BM,6
42
+ Kekik-1.7.3.dist-info/RECORD,,
File without changes
File without changes