nltcache 1.0.6__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.
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: nltcache
3
+ Version: 1.0.6
4
+ Summary: nltcache
5
+ Author-email: niuliangtao <farfarfun@qq.com>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: cachebox==5.0.1
9
+ Requires-Dist: diskcache>=5.6.3
10
+ Requires-Dist: nltlog>=1.0.9
11
+
12
+ # funcache
13
+
14
+ 一个简洁的 Python 函数缓存装饰器库,提供多种缓存策略,涵盖内存缓存、磁盘缓存和 Pickle 文件缓存。
15
+
16
+ ## 安装
17
+
18
+ ```bash
19
+ pip install funcache-tau
20
+ ```
21
+
22
+ 要求 Python >= 3.8。
23
+
24
+ ## 快速开始
25
+
26
+ ```python
27
+ from funcache import lru_cache
28
+
29
+ @lru_cache()
30
+ def fibonacci(n):
31
+ if n < 2:
32
+ return n
33
+ return fibonacci(n - 1) + fibonacci(n - 2)
34
+
35
+ print(fibonacci(50))
36
+ ```
37
+
38
+ ## 内存缓存
39
+
40
+ 基于 [cachebox](https://github.com/awolverp/cachebox) 实现,提供多种淘汰策略。
41
+
42
+ ### cache
43
+
44
+ 最简单的 LRU 缓存装饰器,默认 maxsize=1000,无需传参直接使用。
45
+
46
+ ```python
47
+ from funcache import cache
48
+
49
+ @cache
50
+ def add(a, b):
51
+ return a + b
52
+ ```
53
+
54
+ ### lru_cache
55
+
56
+ **LRU (Least Recently Used)** — 淘汰最久未被访问的缓存条目。
57
+
58
+ ```python
59
+ from funcache import lru_cache
60
+
61
+ @lru_cache(maxsize=500)
62
+ def query(sql):
63
+ ...
64
+ ```
65
+
66
+ ### ttl_cache
67
+
68
+ **TTL (Time To Live)** — 缓存条目在超过指定时间后自动过期。
69
+
70
+ ```python
71
+ from funcache import ttl_cache
72
+
73
+ @ttl_cache(maxsize=1000, ttl=300) # 300 秒后过期
74
+ def get_config(key):
75
+ ...
76
+ ```
77
+
78
+ ### vttl_cache
79
+
80
+ **VTTL (Virtual TTL)** — 与 TTL 类似,但采用惰性淘汰策略,仅在访问时检查并移除过期条目。
81
+
82
+ ```python
83
+ from funcache import vttl_cache
84
+
85
+ @vttl_cache(maxsize=1000, ttl=60)
86
+ def get_status(service):
87
+ ...
88
+ ```
89
+
90
+ ### lfu_cache
91
+
92
+ **LFU (Least Frequently Used)** — 淘汰访问次数最少的缓存条目。
93
+
94
+ ```python
95
+ from funcache import lfu_cache
96
+
97
+ @lfu_cache(maxsize=1000)
98
+ def translate(word):
99
+ ...
100
+ ```
101
+
102
+ ### fifo_cache
103
+
104
+ **FIFO (First In First Out)** — 淘汰最早进入缓存的条目。
105
+
106
+ ```python
107
+ from funcache import fifo_cache
108
+
109
+ @fifo_cache(maxsize=1000)
110
+ def process(data):
111
+ ...
112
+ ```
113
+
114
+ ### rr_cache
115
+
116
+ **RR (Random Replacement)** — 随机淘汰一个缓存条目。
117
+
118
+ ```python
119
+ from funcache import rr_cache
120
+
121
+ @rr_cache(maxsize=1000)
122
+ def compute(x):
123
+ ...
124
+ ```
125
+
126
+ ## 磁盘缓存
127
+
128
+ ### disk_cache
129
+
130
+ 基于 [diskcache](https://github.com/grantjenks/python-diskcache) 实现,将缓存持久化到本地磁盘,支持过期时间,适用于需要跨进程或重启后保留缓存的场景。
131
+
132
+ ```python
133
+ from funcache import disk_cache
134
+
135
+ @disk_cache(cache_key="query", expire=3600) # 缓存 1 小时
136
+ def search(query):
137
+ # 耗时的搜索操作
138
+ ...
139
+
140
+ search("python cache") # 首次执行,结果写入磁盘
141
+ search("python cache") # 命中缓存,直接返回
142
+ ```
143
+
144
+ **参数说明:**
145
+
146
+ | 参数 | 类型 | 默认值 | 说明 |
147
+ |------|------|--------|------|
148
+ | `cache_key` | `str` | *必填* | 用作缓存键的函数参数名 |
149
+ | `cache_dir` | `str \| None` | `None` | 缓存目录,为 None 时自动生成 |
150
+ | `is_cache` | `str` | `"cache"` | 控制是否启用缓存的布尔参数名 |
151
+ | `expire` | `int` | `86400` | 缓存过期时间(秒),默认 1 天 |
152
+
153
+ 通过 `is_cache` 参数可以在运行时动态控制是否使用缓存:
154
+
155
+ ```python
156
+ @disk_cache(cache_key="sql", is_cache="use_cache")
157
+ def run_query(sql, use_cache=True):
158
+ ...
159
+
160
+ run_query("SELECT ...", use_cache=False) # 跳过缓存,直接执行
161
+ ```
162
+
163
+ ## Pickle 文件缓存
164
+
165
+ ### pkl_cache
166
+
167
+ 将函数结果序列化为 `.pkl` 文件存储到本地,适用于需要简单持久缓存但不想引入额外数据库的场景。
168
+
169
+ ```python
170
+ from funcache import pkl_cache
171
+
172
+ @pkl_cache(cache_key="filepath", cache_dir=".my_cache")
173
+ def parse_file(filepath):
174
+ # 耗时的文件解析操作
175
+ ...
176
+
177
+ parse_file("/data/large.csv") # 首次执行,结果写入 .pkl 文件
178
+ parse_file("/data/large.csv") # 命中缓存
179
+ ```
180
+
181
+ **参数说明:**
182
+
183
+ | 参数 | 类型 | 默认值 | 说明 |
184
+ |------|------|--------|------|
185
+ | `cache_key` | `str` | *必填* | 用作缓存键的函数参数名 |
186
+ | `cache_dir` | `str` | `".cache"` | 存储 pkl 文件的目录 |
187
+ | `is_cache` | `str` | `"cache"` | 控制是否启用缓存的布尔参数名 |
188
+ | `printf` | `bool` | `False` | 是否将缓存日志输出到 stdout |
189
+
190
+ ## 其他
191
+
192
+ ### cached_property
193
+
194
+ 重新导出自标准库 `functools.cached_property`,将方法结果缓存为实例属性。
195
+
196
+ ```python
197
+ from funcache import cached_property
198
+
199
+ class Config:
200
+ @cached_property
201
+ def settings(self):
202
+ # 仅在首次访问时执行
203
+ return load_settings()
204
+ ```
205
+
206
+ ## API 一览
207
+
208
+ | 装饰器 | 存储位置 | 淘汰策略 | 支持过期 |
209
+ |--------|---------|---------|---------|
210
+ | `cache` | 内存 | LRU | - |
211
+ | `lru_cache` | 内存 | LRU | - |
212
+ | `lfu_cache` | 内存 | LFU | - |
213
+ | `fifo_cache` | 内存 | FIFO | - |
214
+ | `rr_cache` | 内存 | 随机 | - |
215
+ | `ttl_cache` | 内存 | TTL | 是 |
216
+ | `vttl_cache` | 内存 | VTTL (惰性) | 是 |
217
+ | `disk_cache` | 磁盘 | - | 是 |
218
+ | `pkl_cache` | 磁盘 (pkl) | - | - |
219
+ | `cached_property` | 实例属性 | - | - |
@@ -0,0 +1,208 @@
1
+ # funcache
2
+
3
+ 一个简洁的 Python 函数缓存装饰器库,提供多种缓存策略,涵盖内存缓存、磁盘缓存和 Pickle 文件缓存。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pip install funcache-tau
9
+ ```
10
+
11
+ 要求 Python >= 3.8。
12
+
13
+ ## 快速开始
14
+
15
+ ```python
16
+ from funcache import lru_cache
17
+
18
+ @lru_cache()
19
+ def fibonacci(n):
20
+ if n < 2:
21
+ return n
22
+ return fibonacci(n - 1) + fibonacci(n - 2)
23
+
24
+ print(fibonacci(50))
25
+ ```
26
+
27
+ ## 内存缓存
28
+
29
+ 基于 [cachebox](https://github.com/awolverp/cachebox) 实现,提供多种淘汰策略。
30
+
31
+ ### cache
32
+
33
+ 最简单的 LRU 缓存装饰器,默认 maxsize=1000,无需传参直接使用。
34
+
35
+ ```python
36
+ from funcache import cache
37
+
38
+ @cache
39
+ def add(a, b):
40
+ return a + b
41
+ ```
42
+
43
+ ### lru_cache
44
+
45
+ **LRU (Least Recently Used)** — 淘汰最久未被访问的缓存条目。
46
+
47
+ ```python
48
+ from funcache import lru_cache
49
+
50
+ @lru_cache(maxsize=500)
51
+ def query(sql):
52
+ ...
53
+ ```
54
+
55
+ ### ttl_cache
56
+
57
+ **TTL (Time To Live)** — 缓存条目在超过指定时间后自动过期。
58
+
59
+ ```python
60
+ from funcache import ttl_cache
61
+
62
+ @ttl_cache(maxsize=1000, ttl=300) # 300 秒后过期
63
+ def get_config(key):
64
+ ...
65
+ ```
66
+
67
+ ### vttl_cache
68
+
69
+ **VTTL (Virtual TTL)** — 与 TTL 类似,但采用惰性淘汰策略,仅在访问时检查并移除过期条目。
70
+
71
+ ```python
72
+ from funcache import vttl_cache
73
+
74
+ @vttl_cache(maxsize=1000, ttl=60)
75
+ def get_status(service):
76
+ ...
77
+ ```
78
+
79
+ ### lfu_cache
80
+
81
+ **LFU (Least Frequently Used)** — 淘汰访问次数最少的缓存条目。
82
+
83
+ ```python
84
+ from funcache import lfu_cache
85
+
86
+ @lfu_cache(maxsize=1000)
87
+ def translate(word):
88
+ ...
89
+ ```
90
+
91
+ ### fifo_cache
92
+
93
+ **FIFO (First In First Out)** — 淘汰最早进入缓存的条目。
94
+
95
+ ```python
96
+ from funcache import fifo_cache
97
+
98
+ @fifo_cache(maxsize=1000)
99
+ def process(data):
100
+ ...
101
+ ```
102
+
103
+ ### rr_cache
104
+
105
+ **RR (Random Replacement)** — 随机淘汰一个缓存条目。
106
+
107
+ ```python
108
+ from funcache import rr_cache
109
+
110
+ @rr_cache(maxsize=1000)
111
+ def compute(x):
112
+ ...
113
+ ```
114
+
115
+ ## 磁盘缓存
116
+
117
+ ### disk_cache
118
+
119
+ 基于 [diskcache](https://github.com/grantjenks/python-diskcache) 实现,将缓存持久化到本地磁盘,支持过期时间,适用于需要跨进程或重启后保留缓存的场景。
120
+
121
+ ```python
122
+ from funcache import disk_cache
123
+
124
+ @disk_cache(cache_key="query", expire=3600) # 缓存 1 小时
125
+ def search(query):
126
+ # 耗时的搜索操作
127
+ ...
128
+
129
+ search("python cache") # 首次执行,结果写入磁盘
130
+ search("python cache") # 命中缓存,直接返回
131
+ ```
132
+
133
+ **参数说明:**
134
+
135
+ | 参数 | 类型 | 默认值 | 说明 |
136
+ |------|------|--------|------|
137
+ | `cache_key` | `str` | *必填* | 用作缓存键的函数参数名 |
138
+ | `cache_dir` | `str \| None` | `None` | 缓存目录,为 None 时自动生成 |
139
+ | `is_cache` | `str` | `"cache"` | 控制是否启用缓存的布尔参数名 |
140
+ | `expire` | `int` | `86400` | 缓存过期时间(秒),默认 1 天 |
141
+
142
+ 通过 `is_cache` 参数可以在运行时动态控制是否使用缓存:
143
+
144
+ ```python
145
+ @disk_cache(cache_key="sql", is_cache="use_cache")
146
+ def run_query(sql, use_cache=True):
147
+ ...
148
+
149
+ run_query("SELECT ...", use_cache=False) # 跳过缓存,直接执行
150
+ ```
151
+
152
+ ## Pickle 文件缓存
153
+
154
+ ### pkl_cache
155
+
156
+ 将函数结果序列化为 `.pkl` 文件存储到本地,适用于需要简单持久缓存但不想引入额外数据库的场景。
157
+
158
+ ```python
159
+ from funcache import pkl_cache
160
+
161
+ @pkl_cache(cache_key="filepath", cache_dir=".my_cache")
162
+ def parse_file(filepath):
163
+ # 耗时的文件解析操作
164
+ ...
165
+
166
+ parse_file("/data/large.csv") # 首次执行,结果写入 .pkl 文件
167
+ parse_file("/data/large.csv") # 命中缓存
168
+ ```
169
+
170
+ **参数说明:**
171
+
172
+ | 参数 | 类型 | 默认值 | 说明 |
173
+ |------|------|--------|------|
174
+ | `cache_key` | `str` | *必填* | 用作缓存键的函数参数名 |
175
+ | `cache_dir` | `str` | `".cache"` | 存储 pkl 文件的目录 |
176
+ | `is_cache` | `str` | `"cache"` | 控制是否启用缓存的布尔参数名 |
177
+ | `printf` | `bool` | `False` | 是否将缓存日志输出到 stdout |
178
+
179
+ ## 其他
180
+
181
+ ### cached_property
182
+
183
+ 重新导出自标准库 `functools.cached_property`,将方法结果缓存为实例属性。
184
+
185
+ ```python
186
+ from funcache import cached_property
187
+
188
+ class Config:
189
+ @cached_property
190
+ def settings(self):
191
+ # 仅在首次访问时执行
192
+ return load_settings()
193
+ ```
194
+
195
+ ## API 一览
196
+
197
+ | 装饰器 | 存储位置 | 淘汰策略 | 支持过期 |
198
+ |--------|---------|---------|---------|
199
+ | `cache` | 内存 | LRU | - |
200
+ | `lru_cache` | 内存 | LRU | - |
201
+ | `lfu_cache` | 内存 | LFU | - |
202
+ | `fifo_cache` | 内存 | FIFO | - |
203
+ | `rr_cache` | 内存 | 随机 | - |
204
+ | `ttl_cache` | 内存 | TTL | 是 |
205
+ | `vttl_cache` | 内存 | VTTL (惰性) | 是 |
206
+ | `disk_cache` | 磁盘 | - | 是 |
207
+ | `pkl_cache` | 磁盘 (pkl) | - | - |
208
+ | `cached_property` | 实例属性 | - | - |
@@ -0,0 +1,13 @@
1
+ [project]
2
+ name = "nltcache"
3
+ version = "1.0.6"
4
+ description = "nltcache"
5
+ readme = "README.md"
6
+ requires-python = ">=3.8"
7
+ dependencies = [ "cachebox==5.0.1", "diskcache>=5.6.3", "nltlog>=1.0.9",]
8
+ [[project.authors]]
9
+ name = "niuliangtao"
10
+ email = "farfarfun@qq.com"
11
+
12
+ [tool.setuptools]
13
+ license-files = []
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,26 @@
1
+ from .box import (
2
+ cache,
3
+ fifo_cache,
4
+ lfu_cache,
5
+ lru_cache,
6
+ rr_cache,
7
+ ttl_cache,
8
+ vttl_cache,
9
+ )
10
+ from .core import PickleCache, cached_property, pkl_cache
11
+ from .disk import DiskCache, disk_cache
12
+
13
+ __all__ = [
14
+ "cache",
15
+ "lru_cache",
16
+ "ttl_cache",
17
+ "vttl_cache",
18
+ "lfu_cache",
19
+ "fifo_cache",
20
+ "rr_cache",
21
+ "PickleCache",
22
+ "pkl_cache",
23
+ "cached_property",
24
+ "disk_cache",
25
+ "DiskCache",
26
+ ]
@@ -0,0 +1,30 @@
1
+ """Internal utilities shared across funcache modules."""
2
+
3
+ import inspect
4
+ import os
5
+ from typing import Any, Callable, Dict, Tuple
6
+
7
+
8
+ def normalize_args(
9
+ func: Callable, args: Tuple[Any, ...], kwargs: Dict[str, Any]
10
+ ) -> Dict[str, Any]:
11
+ """Convert positional arguments to keyword arguments based on function signature.
12
+
13
+ This merges positional args into a unified kwargs dict for consistent
14
+ cache key lookup by parameter name.
15
+ """
16
+ merged = dict(kwargs)
17
+ params = list(inspect.signature(func).parameters.items())
18
+ for i, (name, param) in enumerate(params):
19
+ if name not in merged:
20
+ merged[name] = args[i] if i < len(args) else param.default
21
+ return merged
22
+
23
+
24
+ def ensure_gitignore(directory: str) -> None:
25
+ """Create a .gitignore with '*' in the given directory if one doesn't exist."""
26
+ os.makedirs(directory, exist_ok=True)
27
+ ignore_file = os.path.join(directory, ".gitignore")
28
+ if not os.path.exists(ignore_file):
29
+ with open(ignore_file, "w") as f:
30
+ f.write("*")
@@ -0,0 +1,66 @@
1
+ from typing import Callable
2
+
3
+ from cachebox import FIFOCache, LFUCache, LRUCache, RRCache, TTLCache, VTTLCache, cached
4
+
5
+ __all__ = [
6
+ "cache",
7
+ "lru_cache",
8
+ "ttl_cache",
9
+ "vttl_cache",
10
+ "lfu_cache",
11
+ "fifo_cache",
12
+ "rr_cache",
13
+ ]
14
+
15
+
16
+ def cache(func: Callable, /) -> Callable:
17
+ """Simple LRU cache decorator with a default maxsize of 1000."""
18
+ return cached(LRUCache(maxsize=1000))(func)
19
+
20
+
21
+ def vttl_cache(maxsize: int = 1000, ttl: int = 60) -> Callable:
22
+ """VTTLCache: removes expired elements lazily when accessed.
23
+
24
+ VTTLCache: 在访问时才惰性移除已过期的缓存元素。
25
+ """
26
+ return lambda func: cached(VTTLCache(maxsize=maxsize, ttl=ttl))(func)
27
+
28
+
29
+ def ttl_cache(maxsize: int = 1000, ttl: int = 60) -> Callable:
30
+ """TTLCache: automatically removes expired elements.
31
+
32
+ TTLCache:自动移除已过期的缓存元素。
33
+ """
34
+ return lambda func: cached(TTLCache(maxsize=maxsize, ttl=ttl))(func)
35
+
36
+
37
+ def lru_cache(maxsize: int = 1000) -> Callable:
38
+ """LRUCache: removes the least recently used element.
39
+
40
+ LRUCache:移除缓存中自上次访问以来时间最长的元素。
41
+ """
42
+ return lambda func: cached(LRUCache(maxsize=maxsize))(func)
43
+
44
+
45
+ def lfu_cache(maxsize: int = 1000) -> Callable:
46
+ """LFUCache: removes the least frequently used element.
47
+
48
+ LFUCache:移除缓存中访问次数最少的元素,不论其访问时间。
49
+ """
50
+ return lambda func: cached(LFUCache(maxsize=maxsize))(func)
51
+
52
+
53
+ def fifo_cache(maxsize: int = 1000) -> Callable:
54
+ """FIFOCache: removes the oldest element.
55
+
56
+ FIFOCache:移除在缓存中停留时间最长的元素。
57
+ """
58
+ return lambda func: cached(FIFOCache(maxsize=maxsize))(func)
59
+
60
+
61
+ def rr_cache(maxsize: int = 1000) -> Callable:
62
+ """RRCache: randomly removes an element when space is needed.
63
+
64
+ RRCache: 在必要时随机选择一个元素进行移除,以腾出空间。
65
+ """
66
+ return lambda func: cached(RRCache(maxsize=maxsize))(func)
@@ -0,0 +1,97 @@
1
+ import hashlib
2
+ import os
3
+ import pickle
4
+ from functools import cached_property, wraps
5
+ from typing import Any, Callable, Optional
6
+
7
+ from funlog import getLogger
8
+
9
+ from ._utils import ensure_gitignore, normalize_args
10
+
11
+ logger = getLogger("funcache")
12
+
13
+ __all__ = ["PickleCache", "pkl_cache", "cached_property"]
14
+
15
+
16
+ class PickleCache:
17
+ """Decorator that caches function results to pickle files on disk.
18
+
19
+ Args:
20
+ cache_key: The name of the function parameter whose value is used as the cache key.
21
+ cache_dir: Directory to store pickle cache files.
22
+ is_cache: The name of a boolean parameter that controls whether caching is enabled.
23
+ printf: If True, also print cache log messages to stdout.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ cache_key: str,
29
+ cache_dir: str = ".cache",
30
+ is_cache: str = "cache",
31
+ printf: bool = False,
32
+ ) -> None:
33
+ self.cache_key = cache_key
34
+ self.cache_dir = cache_dir
35
+ self.is_cache = is_cache
36
+ self.printf = printf
37
+
38
+ def _log(self, msg: str) -> None:
39
+ if self.printf:
40
+ print(msg)
41
+ logger.debug(msg)
42
+
43
+ def _get_cache_file(self, key: str) -> str:
44
+ hashed = hashlib.md5(str(key).encode()).hexdigest()
45
+ return os.path.join(self.cache_dir, f"{hashed}.pkl")
46
+
47
+ @staticmethod
48
+ def _load_cache(cache_file: str) -> Optional[Any]:
49
+ try:
50
+ with open(cache_file, "rb") as f:
51
+ return pickle.load(f)
52
+ except (FileNotFoundError, pickle.PickleError):
53
+ return None
54
+
55
+ def _save_cache(self, cache_file: str, data: Any) -> None:
56
+ ensure_gitignore(self.cache_dir)
57
+ with open(cache_file, "wb") as f:
58
+ pickle.dump(data, f)
59
+
60
+ def __call__(self, func: Callable) -> Callable:
61
+ @wraps(func)
62
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
63
+ merged = normalize_args(func, args, kwargs)
64
+
65
+ is_cache = merged.get(self.is_cache, True)
66
+ cache_key = merged.get(self.cache_key, "")
67
+ is_cache = is_cache and cache_key is not None
68
+
69
+ if is_cache:
70
+ cache_file = self._get_cache_file(cache_key)
71
+ cached_result = self._load_cache(cache_file)
72
+ if cached_result is not None:
73
+ self._log(
74
+ f"Cache hit for function '{func.__name__}' with key: {cache_key}"
75
+ )
76
+ return cached_result
77
+
78
+ result = func(**merged)
79
+
80
+ if is_cache:
81
+ self._save_cache(cache_file, result)
82
+ self._log(
83
+ f"Cache data for function '{func.__name__}' with key: {cache_key}"
84
+ )
85
+ return result
86
+
87
+ return wrapper
88
+
89
+
90
+ def pkl_cache(
91
+ cache_key: str,
92
+ cache_dir: str = ".cache",
93
+ is_cache: str = "cache",
94
+ printf: bool = False,
95
+ ) -> PickleCache:
96
+ """Convenience factory for :class:`PickleCache`."""
97
+ return PickleCache(cache_key, cache_dir, is_cache, printf=printf)
@@ -0,0 +1,97 @@
1
+ import os
2
+ from functools import wraps
3
+ from hashlib import md5
4
+ from typing import Any, Callable, Optional
5
+
6
+ from diskcache import Cache
7
+ from funlog import getLogger
8
+
9
+ from ._utils import ensure_gitignore, normalize_args
10
+
11
+ logger = getLogger("funcache")
12
+
13
+ __all__ = ["DiskCache", "disk_cache"]
14
+
15
+
16
+ class DiskCache:
17
+ """Decorator that caches function results using :class:`diskcache.Cache`.
18
+
19
+ Args:
20
+ cache_key: The name of the function parameter whose value is used as the cache key.
21
+ cache_dir: Directory for the disk cache. Auto-generated from the function if *None*.
22
+ is_cache: The name of a boolean parameter that controls whether caching is enabled.
23
+ expire: Cache entry expiration time in seconds (default: 1 day).
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ cache_key: str,
29
+ cache_dir: Optional[str] = None,
30
+ is_cache: str = "cache",
31
+ expire: int = 60 * 60 * 24,
32
+ ) -> None:
33
+ self.cache_key = cache_key
34
+ self.cache_dir = cache_dir
35
+ self.is_cache = is_cache
36
+ self.expire = expire
37
+ self._cache: Optional[Cache] = None
38
+
39
+ def _init_cache(self, func: Callable) -> Cache:
40
+ if self._cache is not None:
41
+ return self._cache
42
+
43
+ if self.cache_dir is None:
44
+ uid = md5(func.__code__.co_filename.encode("utf-8")).hexdigest()
45
+ self.cache_dir = os.path.join(".disk_cache", f"{uid}-{func.__name__}")
46
+
47
+ self._cache = Cache(self.cache_dir)
48
+ ensure_gitignore(self.cache_dir)
49
+
50
+ logger.success(
51
+ f"init func {func.__name__} success. with cache_dir: {self.cache_dir}"
52
+ )
53
+ return self._cache
54
+
55
+ def __call__(self, func: Callable) -> Callable:
56
+ cache = self._init_cache(func)
57
+
58
+ @wraps(func)
59
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
60
+ merged = normalize_args(func, args, kwargs)
61
+
62
+ cache_key = merged.get(self.cache_key, "")
63
+ is_cache = merged.get(self.is_cache, True) and cache_key is not None
64
+
65
+ if not is_cache:
66
+ return func(**merged)
67
+
68
+ cached_result = cache.get(cache_key)
69
+ if cached_result is not None:
70
+ logger.debug(
71
+ f"Cache hit for function '{func.__name__}' with key: {cache_key}"
72
+ )
73
+ return cached_result
74
+
75
+ result = func(**merged)
76
+ cache.set(cache_key, result, expire=self.expire)
77
+ logger.debug(
78
+ f"Cache data for function '{func.__name__}' with key: {cache_key}"
79
+ )
80
+ return result
81
+
82
+ return wrapper
83
+
84
+
85
+ def disk_cache(
86
+ cache_key: str,
87
+ cache_dir: Optional[str] = None,
88
+ is_cache: str = "cache",
89
+ expire: int = 60 * 60 * 24,
90
+ ) -> DiskCache:
91
+ """Convenience factory for :class:`DiskCache`."""
92
+ return DiskCache(
93
+ cache_key=cache_key,
94
+ cache_dir=cache_dir,
95
+ is_cache=is_cache,
96
+ expire=expire,
97
+ )
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: nltcache
3
+ Version: 1.0.6
4
+ Summary: nltcache
5
+ Author-email: niuliangtao <farfarfun@qq.com>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: cachebox==5.0.1
9
+ Requires-Dist: diskcache>=5.6.3
10
+ Requires-Dist: nltlog>=1.0.9
11
+
12
+ # funcache
13
+
14
+ 一个简洁的 Python 函数缓存装饰器库,提供多种缓存策略,涵盖内存缓存、磁盘缓存和 Pickle 文件缓存。
15
+
16
+ ## 安装
17
+
18
+ ```bash
19
+ pip install funcache-tau
20
+ ```
21
+
22
+ 要求 Python >= 3.8。
23
+
24
+ ## 快速开始
25
+
26
+ ```python
27
+ from funcache import lru_cache
28
+
29
+ @lru_cache()
30
+ def fibonacci(n):
31
+ if n < 2:
32
+ return n
33
+ return fibonacci(n - 1) + fibonacci(n - 2)
34
+
35
+ print(fibonacci(50))
36
+ ```
37
+
38
+ ## 内存缓存
39
+
40
+ 基于 [cachebox](https://github.com/awolverp/cachebox) 实现,提供多种淘汰策略。
41
+
42
+ ### cache
43
+
44
+ 最简单的 LRU 缓存装饰器,默认 maxsize=1000,无需传参直接使用。
45
+
46
+ ```python
47
+ from funcache import cache
48
+
49
+ @cache
50
+ def add(a, b):
51
+ return a + b
52
+ ```
53
+
54
+ ### lru_cache
55
+
56
+ **LRU (Least Recently Used)** — 淘汰最久未被访问的缓存条目。
57
+
58
+ ```python
59
+ from funcache import lru_cache
60
+
61
+ @lru_cache(maxsize=500)
62
+ def query(sql):
63
+ ...
64
+ ```
65
+
66
+ ### ttl_cache
67
+
68
+ **TTL (Time To Live)** — 缓存条目在超过指定时间后自动过期。
69
+
70
+ ```python
71
+ from funcache import ttl_cache
72
+
73
+ @ttl_cache(maxsize=1000, ttl=300) # 300 秒后过期
74
+ def get_config(key):
75
+ ...
76
+ ```
77
+
78
+ ### vttl_cache
79
+
80
+ **VTTL (Virtual TTL)** — 与 TTL 类似,但采用惰性淘汰策略,仅在访问时检查并移除过期条目。
81
+
82
+ ```python
83
+ from funcache import vttl_cache
84
+
85
+ @vttl_cache(maxsize=1000, ttl=60)
86
+ def get_status(service):
87
+ ...
88
+ ```
89
+
90
+ ### lfu_cache
91
+
92
+ **LFU (Least Frequently Used)** — 淘汰访问次数最少的缓存条目。
93
+
94
+ ```python
95
+ from funcache import lfu_cache
96
+
97
+ @lfu_cache(maxsize=1000)
98
+ def translate(word):
99
+ ...
100
+ ```
101
+
102
+ ### fifo_cache
103
+
104
+ **FIFO (First In First Out)** — 淘汰最早进入缓存的条目。
105
+
106
+ ```python
107
+ from funcache import fifo_cache
108
+
109
+ @fifo_cache(maxsize=1000)
110
+ def process(data):
111
+ ...
112
+ ```
113
+
114
+ ### rr_cache
115
+
116
+ **RR (Random Replacement)** — 随机淘汰一个缓存条目。
117
+
118
+ ```python
119
+ from funcache import rr_cache
120
+
121
+ @rr_cache(maxsize=1000)
122
+ def compute(x):
123
+ ...
124
+ ```
125
+
126
+ ## 磁盘缓存
127
+
128
+ ### disk_cache
129
+
130
+ 基于 [diskcache](https://github.com/grantjenks/python-diskcache) 实现,将缓存持久化到本地磁盘,支持过期时间,适用于需要跨进程或重启后保留缓存的场景。
131
+
132
+ ```python
133
+ from funcache import disk_cache
134
+
135
+ @disk_cache(cache_key="query", expire=3600) # 缓存 1 小时
136
+ def search(query):
137
+ # 耗时的搜索操作
138
+ ...
139
+
140
+ search("python cache") # 首次执行,结果写入磁盘
141
+ search("python cache") # 命中缓存,直接返回
142
+ ```
143
+
144
+ **参数说明:**
145
+
146
+ | 参数 | 类型 | 默认值 | 说明 |
147
+ |------|------|--------|------|
148
+ | `cache_key` | `str` | *必填* | 用作缓存键的函数参数名 |
149
+ | `cache_dir` | `str \| None` | `None` | 缓存目录,为 None 时自动生成 |
150
+ | `is_cache` | `str` | `"cache"` | 控制是否启用缓存的布尔参数名 |
151
+ | `expire` | `int` | `86400` | 缓存过期时间(秒),默认 1 天 |
152
+
153
+ 通过 `is_cache` 参数可以在运行时动态控制是否使用缓存:
154
+
155
+ ```python
156
+ @disk_cache(cache_key="sql", is_cache="use_cache")
157
+ def run_query(sql, use_cache=True):
158
+ ...
159
+
160
+ run_query("SELECT ...", use_cache=False) # 跳过缓存,直接执行
161
+ ```
162
+
163
+ ## Pickle 文件缓存
164
+
165
+ ### pkl_cache
166
+
167
+ 将函数结果序列化为 `.pkl` 文件存储到本地,适用于需要简单持久缓存但不想引入额外数据库的场景。
168
+
169
+ ```python
170
+ from funcache import pkl_cache
171
+
172
+ @pkl_cache(cache_key="filepath", cache_dir=".my_cache")
173
+ def parse_file(filepath):
174
+ # 耗时的文件解析操作
175
+ ...
176
+
177
+ parse_file("/data/large.csv") # 首次执行,结果写入 .pkl 文件
178
+ parse_file("/data/large.csv") # 命中缓存
179
+ ```
180
+
181
+ **参数说明:**
182
+
183
+ | 参数 | 类型 | 默认值 | 说明 |
184
+ |------|------|--------|------|
185
+ | `cache_key` | `str` | *必填* | 用作缓存键的函数参数名 |
186
+ | `cache_dir` | `str` | `".cache"` | 存储 pkl 文件的目录 |
187
+ | `is_cache` | `str` | `"cache"` | 控制是否启用缓存的布尔参数名 |
188
+ | `printf` | `bool` | `False` | 是否将缓存日志输出到 stdout |
189
+
190
+ ## 其他
191
+
192
+ ### cached_property
193
+
194
+ 重新导出自标准库 `functools.cached_property`,将方法结果缓存为实例属性。
195
+
196
+ ```python
197
+ from funcache import cached_property
198
+
199
+ class Config:
200
+ @cached_property
201
+ def settings(self):
202
+ # 仅在首次访问时执行
203
+ return load_settings()
204
+ ```
205
+
206
+ ## API 一览
207
+
208
+ | 装饰器 | 存储位置 | 淘汰策略 | 支持过期 |
209
+ |--------|---------|---------|---------|
210
+ | `cache` | 内存 | LRU | - |
211
+ | `lru_cache` | 内存 | LRU | - |
212
+ | `lfu_cache` | 内存 | LFU | - |
213
+ | `fifo_cache` | 内存 | FIFO | - |
214
+ | `rr_cache` | 内存 | 随机 | - |
215
+ | `ttl_cache` | 内存 | TTL | 是 |
216
+ | `vttl_cache` | 内存 | VTTL (惰性) | 是 |
217
+ | `disk_cache` | 磁盘 | - | 是 |
218
+ | `pkl_cache` | 磁盘 (pkl) | - | - |
219
+ | `cached_property` | 实例属性 | - | - |
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/nltcache/__init__.py
4
+ src/nltcache/_utils.py
5
+ src/nltcache/box.py
6
+ src/nltcache/core.py
7
+ src/nltcache/disk.py
8
+ src/nltcache.egg-info/PKG-INFO
9
+ src/nltcache.egg-info/SOURCES.txt
10
+ src/nltcache.egg-info/dependency_links.txt
11
+ src/nltcache.egg-info/requires.txt
12
+ src/nltcache.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ cachebox==5.0.1
2
+ diskcache>=5.6.3
3
+ nltlog>=1.0.9
@@ -0,0 +1 @@
1
+ nltcache