Name | pycorelibs JSON |
Version |
0.2.7
JSON |
| download |
home_page | None |
Summary | A collection of reusable python core library from AI Lingues. |
upload_time | 2025-10-23 04:52:54 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.11 |
license | MIT |
keywords |
ailingues
components
core
library
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# PyCoreLibs
AI Lingues 基础组件库- PyCoreLibs
## Message
消息库,基于redis的异步消息框架
参考:redis/README.md
## Network
### search 搜索引擎
互联网搜索引擎
### requests 请求
- fetch_url 强化版通用网络请求函数,增加了重试机制,并适用于复杂场景。
- is_url 验证是否为合法url
### scrawer 网页文本获取
- HTMLContent 提取网页内容
## security 模块
### base64s 模块
### cryptographys 模块
----
#### AsymmetricCrypto: 稳健可靠的非对称加解密 + 签名/验签 工具类
##### 说明
- 加解密:RSA-OAEP(SHA-256) + AES-GCM(256) 的混合加密(任意长度数据)
- 签名:RSA-PSS(SHA-256),返回 JSON 打包(Base64)
- 设计目标:健壮、跨语言友好(JSON+Base64)、异常统一封装
|函数 |说明 |
|-------------------|------------------------------|
|`generate_keypair` | 生成密钥对(PEM) |
|`encrypt` |公钥加密(混合加密) |
|`decrypt` |私钥解密 |
|`sign` |私钥签名(RSA-PSS + SHA-256) |
|`verify` |公钥验签 |
##### 示例
```python
from pycorelibs.security.cryptographys import AsymmetricCrypto
message = b"hello, asymmetric crypto! \xf0\x9f\x94\x90"
# 1) 生成密钥对(内存)
priv, pub = AsymmetricCrypto.generate_keypair(
key_size=2048, private_password=b"pass"
)
# 2) 加密(可选 AAD)
aad = b"meta:example"
blob = AsymmetricCrypto.encrypt(message, pub, aad=aad)
# 3) 解密
plain = AsymmetricCrypto.decrypt(blob, priv, private_password=b"pass")
assert plain == message
print("Decrypt OK:", plain.decode("utf-8"))
# 4) 签名
sig_blob = AsymmetricCrypto.sign(message, priv, private_password=b"pass")
# 5) 验签(成功)
ok = AsymmetricCrypto.verify(message, sig_blob, pub)
print("Verify OK:", ok)
# 6) 验签(失败示例:数据被改动)
tampered = message + b"!"
bad = AsymmetricCrypto.verify(tampered, sig_blob, pub)
print("Verify with tamper:", bad)
```
----
##### generate_keypair
生成 RSA 密钥对(PKCS#8 私钥 + X.509 公钥,PEM 格式)
###### 参数说明
- key_size (int, optional)
2048/3072/4096(推荐 2048+). Defaults to 2048.
- private_password (Optional[bytes], optional)
给私钥加密的口令(None 表示不加密). Defaults to None.
- save_to (Optional[str], optional)
可选保存目录(写入 private.pem / public.pem). Defaults to None.
###### Raises
- ValueError:
RSA key_size 仅支持 2048/3072/4096。
- AsymmetricCryptoError:
生成密钥对失败
###### Returns
- Tuple[bytes, bytes]
生成 RSA 密钥对(PKCS#8 私钥 + X.509 公钥,PEM 格式)
----
### fingerprints 模块
跨平台硬件指纹
- 只用 Python 库,不走系统命令;信息更稳定
- 多网卡:枚举全部,带过滤选项(是否包含虚拟/回环、是否只取UP、是否要求有IP)
- 主 MAC 选择策略可配置
- 生成哈希指纹(可传入 salt 提高伪造成本)
#### generate_hardware_fingerprint
生成硬件指纹(不包含TPM)
##### 参数说明
- isGPU (bool):
是否获取GPU信息,缺省为False
- salt (Optional[str], optional):
额外盐值(字符串)。如用于授权/反爬,建议传入,提高伪造成本。. Defaults to None.
##### Returns
Dict[str, Any]: {
"fid": "\<sha512 hex\>",
"hash": "sha512",
"input": data,
}
- "fid": "\<sha512 hex\>"
最终指纹ID,长度128个字符
- "hash": "sha512"
固定标记,加密算法名
- input": data
加密原始数据
##### 示例
```python
from pycorelibs.security.fingerprints import generate_hardware_fingerprint
# 典型:只要UP、排除虚拟和回环;无IP也可(离线机器也能给出指纹)
fp = generate_hardware_fingerprint()
print(fp)
print("*" * 80)
# 更“联网语义”:要求接口必须有非回环IP
fp_net = generate_hardware_fingerprint(require_ip=True)
print(fp_net)
print("*" * 80)
# 有授权/反爬需求:加盐
fp_salted = generate_hardware_fingerprint(salt="your-secret-salt-123")
print(fp_salted)
print("*" * 80)
```
### md5s 模块
#### md5
计算输入内容的 MD5 值
##### 参数说明
- text (Any):
任意可转为字符串的输入内容。
- length (int):
结果长度,可选 16 或 32(默认 32)。
- isHex (bool):
是否以十六进制格式输出(默认为 True)。
##### 返回
- (bool, Optional[str])
成功与否、以及 MD5 值字符串或 None。
----
## system模块
获取当前系统信息
- OS 操作系统
- CPU
- Macs 网卡
- MB 主板
### runtime 运行期信息模块
获取运行期相关信息
#### RunTimeInfo类
##### get_boot_time
获取系统启动时间和系统已运行时长
跨平台细节:
- Linux:boot_time 基本来自 btime(自启动以来的挂钟差,包含睡眠/休眠的停留时间)。
- Windows:psutil 内部通常用系统 tick 估算,一般不包含睡眠时间(更接近“活跃运行时长”)。
###### 参数说明
- fmt (Optional[str], optional):
时间格式化字符串,默认为 None 使用当前系统设定时区的本地格式.
###### Returns
- Dict[str, Any]:
包含启动时间戳、格式化时间、已运行秒数和可读格式
----
###### get_uptime
获取自 start_mono(monotonic 起点)至今的运行时长(不受系统时钟跳变影响,如被 NTP/手动调整,或夏令时切换)
###### 参数说明
- start_mono (float, optional):
计算开始时间. Defaults to _SERVICE_START_.
- fmt (Optional[str], optional):
时间格式化字符串,默认为 None 使用当前系统设定时区的本地格式.. Defaults to None.
###### Returns
- Dict[str, Any]:
包含启动时间戳、格式化时间、已运行秒数和可读格式
----
## Utils
----
### config配置模块
通用的本地配置存取层(与数据结构无关)
1. 面向任意Python对象:通过可插拔 Serializer 适配(JSON/YAML/TOML/Pickle/自定义均可)
2. 原子写入、自动创建目录、可选自动保存
3. 多命名空间:各自文件、各自序列化器,互不干扰
4. 默认保存到“应用同目录”
5. 支持配置文件加密存储
#### ConfigRepo类
仓库:管理多个命名空间。
一个应用一个仓库,里面可注册多个命名空间。
每个命名空间:独立文件 + 独立序列化器 + 独立默认值
##### 构造函数
```python
__init__(self, root: Optional[str] = None, subdir: Optional[str] = None)
```
**作用**:创建一个配置仓库实例,确定所有命名空间文件的根目录。
###### 参数说明
- root:str | None
仓库根目录的绝对/相对路径。若为 None,则自动解析为**应用同目录**:
- PyInstaller 打包后:os.path.dirname(sys.executable)
- 普通脚本:os.path.dirname(os.path.abspath(sys.argv[0] or __file__))
- subdir:str | None
root 下的子目录名(例如 "config"),用于把配置集中到 root/subdir。
###### 返回
无。
###### 副作用
若目录不存在会自动创建。
###### 示例
```python
# 保存到应用目录下的 ./config/
repo = ConfigRepo(subdir="config")
# 保存到指定绝对路径
repo = ConfigRepo(root="D:/myapp/config")
```
- register注册命名空间
```python
register(self, name: str, serializer: Serializer[Any], filename: Optional[str] = None, default_factory: Optional[Callable[[], Any]] = None, autosave: bool = False, backup_on_save: bool = False, cipher: Optional[AesGcmBox] = None, cipher_policy: str = "auto", encrypt_on_save_if_cipher: bool = True) -> Namespace[Any]
```
**作用**:
注册一个命名空间(即一个逻辑“配置空间”),绑定文件、序列化器、默认值与加密策略。
**参数**:
- name:str
命名空间名称(在仓库内唯一)。
- serializer:Serializer[Any]
序列化器实例:如 JsonSerializer()、PickleSerializer() 或自定义实现(需提供 dumps(obj)->bytes 与 loads(bytes)->obj)。
- filename:str | None
文件名。若省略:当 serializer 为 JsonSerializer 时默认 "<name>.json",否则为 "<name>.bin"。
- default_factory:() -> Any | None
当文件不存在或需重置时,用此函数惰性生成默认对象(如 dict/list/自定义对象)。若省略,默认值为 None。
- autosave:bool
设为 True 时,调用 set() / update() / reset() 等修改后会自动保存到文件。
- backup_on_save:bool
保存前若目标文件已存在,将其重命名为 *.bak 作为备份。
- cipher:AesGcmBox | None
可选的加密盒;提供则进行加密存储(AES-GCM)。不提供则明文存储。
- cipher_policy:"auto" | "require"(默认 "auto")
- "auto":读取时若检测到加密魔数则解密,否则按明文加载(兼容旧文件);
- "require":强制要求文件为加密格式,若无魔数直接抛错(适合敏感空间)。
- encrypt_on_save_if_cipher:bool(默认 True)
当 cipher 存在且此前是明文加载的旧文件,保存时会自动迁移为加密格式。
**返回**:
Namespace[Any]:已注册的命名空间对象。
**异常**:
无(但后续加载/保存时可能抛出序列化/IO/解密类异常)。
**示例**:
```python
# 明文 JSON 偏好设置
ui = repo.register(
name="ui",
serializer=JsonSerializer(),
filename="ui.json",
default_factory=lambda: {"theme": "light", "recent_files": []},
autosave=True
)
# 加密的 Pickle 缓存(旧明文文件可兼容读取,保存后自动迁移为密文)
cache = repo.register(
name="cache",
serializer=PickleSerializer(),
filename="cache.dat",
default_factory=list,
autosave=True,
cipher=AesGcmBox(password=os.environ["CACHE_PASSWORD"]),
cipher_policy="auto",
encrypt_on_save_if_cipher=True,
)
```
----
- space 取命名空间对象
```python
space(self, name: str) -> Namespace[Any]
```
**作用**:按名称获取已注册的命名空间对象。
**参数**:
- name:str
注册时使用的命名空间名称。
**返回**:
- Namespace[Any]:命名空间对象。
**异常**:
- KeyError:当 name 尚未注册时抛出。
**示例**:
```python
cache_ns = repo.space("cache")
```
----
- get直接存取对象(便捷方法)
```python
get(self, name: str) -> Any
```
**作用**:从指定命名空间读取对象(内部若未加载会自动调用 Namespace.load())。
**参数**:
- name:str
**返回**:
- Any:命名空间中的对象(可能是 dict/list/自定义对象/None 等)。
**异常**:
- KeyError:命名空间未注册;
- 反序列化/解密/IO 异常:底层抛出的 ValueError、OSError 等。
**示例**:
```python
ui_data = repo.get("ui") # 例如: {"theme": "light", "recent_files": []}
```
----
- set(self, name: str, value: Any, save: Optional[bool] = None) -> None
**作用**:设置命名空间对象并可选择保存。
**参数**:
- name:str
- value:Any
要写入的对象(会通过绑定的 serializer 持久化)。
- save:bool | None
**True**:立即保存;
**False**:只更新内存缓存,不落盘;
**None**:跟随命名空间的 autosave 设置(若为 True 则保存)。
**返回**:无。
**异常**:同 get()。
**示例**:
```python
repo.set("ui", {"theme": "dark"}, save=True)
```
----
- update(self, name: str, mutator: Callable[[Any], Any], save: Optional[bool] = None) -> Any
**作用**:在原对象基础上以函数式方式变换并回写(读 -> 变换 -> 写)。
**参数**:
- name:str
- mutator:(obj: Any) -> Any
入参是当前对象,返回变换后的新对象(函数内应返回新值)。
- save:bool | None
语义同 set() 的 save。
**返回**:
- Any:变换后的新对象。
**异常**:同 get()/set()。
**示例**:
```python
def add_recent(d):
d = d or {}
rec = (d.get("recent_files") or []) + ["a.sdf"]
return {**d, "recent_files": rec[-10:]} # 只保留最近10条
new_ui = repo.update("ui", add_recent, save=True)
```
----
- 批量操作
save_all(self) -> None
**作用**:对所有“已加载过”的命名空间执行 save()(避免无意义的空写)。
**参数**:无。
**返回**:无。
**异常**:底层保存可能抛出 IO/序列化/加密相关异常。
注意:
仅对内部标记 _loaded == True 的空间生效;
若你需要确保所有空间都有初值并保存,可先 load_all() 或逐个 space(...).get() 激活。
**示例**:
```python
# 某些空间 autosave=False,集中一次性保存
repo.save_all()
```
----
- load_all(self) -> None
**作用**:对所有已注册的命名空间执行 load()(将文件内容加载到内存缓存)。
**参数**:无。
**返回**:无。
**异常**:底层加载可能抛出 IO/反序列化/解密相关异常。
**示例**:
```python
# 应用启动时预热全部配置
repo.load_all()
```
#### 常见问题与注意事项
- 路径选择:
默认落在应用同目录,便于“就地携带”。如需跨用户共享或写权限受限(Windows Program Files),建议显式指定 root。
- 默认值:
default_factory 推荐传入 可调用(如 dict/list/lambda),避免可变对象作为默认参数带来的共享副作用。
- 加密策略:
- **"auto"**:适合从旧明文平滑迁移为密文;
- **"require"**:适合强约束的敏感空间(若非加密立刻报错)。
- 备份文件:
backup_on_save=True 时会生成 *.bak;其内容可能为上一次保存时的明文或密文,读取时可用 AesGcmBox.is_encrypted() 判别。
- 异常处理:
解密失败通常是口令/密钥不匹配或文件损坏;请捕获 ValueError 并引导用户检查口令来源。
- 线程/进程并发:
当前实现是单进程内使用的简单本地层;若存在多进程/多实例同时写入同一文件,请自行加锁或采用更高级别的存储方案。
#### 典型用法示例(整合)
```python
from pycorelibs.utils.config import ConfigRepo, JsonSerializer, PickleSerializer
from pycorelibs.security.aesgcmbox import AesGcmBox
import os
repo = ConfigRepo(subdir="config")
# 明文 UI 配置
ui = repo.register(
name="ui",
serializer=JsonSerializer(),
filename="ui.json",
default_factory=lambda: {"theme": "light", "recent_files": []},
autosave=True,
)
# 加密的缓存(从旧明文自动迁移)
cache = repo.register(
name="cache",
serializer=PickleSerializer(),
filename="cache.dat",
default_factory=list,
autosave=True,
cipher=AesGcmBox(password=os.environ.get("CACHE_PW", "CHANGE_ME")),
cipher_policy="auto",
encrypt_on_save_if_cipher=True,
)
# 读取 / 更新 / 批量保存
ui_data = repo.get("ui")
repo.update("ui", lambda d: {**(d or {}), "theme": "dark"})
repo.update("cache", lambda lst: (lst or []) + ["session#1"])
repo.save_all()
```
----
### files文件处理
- EnumFileSizeUnit
- FilesInfo 类
- get_file_size
获取文件大小,并返回指定单位的大小。
支持B,KB,MB,GB,TB
- line_count
获取指定文件的总行数
Windows系统: 使用换行符作为判断标记
Linux系统: 使用系统自带的wc命令统计
注: 近适用于文本类型文件,其他类型如二进制文件统计结果不具备参考意义
- get_directory_last_modified_time
获取指定目录最后修改时间
- get_directory_last_modified_timestamp
获取指定目录最后修改时间戳
### formate 数据格式化指定类型
Formates 类,静态方法如下:
- fInt 转换为整数
- fFloat 转换为浮点数
- fBool 转换为布尔值
- fDate 按指定格式解析日期
- fEnum 校验值是否在允许集合内(用于枚举字段)
- fJSON 解析为 JSON 对象
### logger日志
- 支持自动产生日志
- 支持同时向控制台和日志文件输出
- 支持进度条
- 支持info、warning和error三个级别的日志
### strings 字符串处理
- UniCodeGenerator 模块
创建随机码,日期前缀 + 随机码
可配置、使用 secrets 生成高强度随机码的编码生成类
| 场景 | 参数示例 |
| ----------------| ---------------------------------------------- |
| 邀请码(短) | `prefix_date=False, random_length=6` |
| 订单号(当天唯一)| `prefix_date=True, random_length=4` |
| URL 安全短码 | `charset=string.ascii_letters + string.digits` |
- json_dumps_bytes 函数
将 dict 序列化为紧凑 UTF-8 JSON bytes
- json_loads_bytes 函数
将 UTF-8 JSON bytes 解析为 dict,并进行基本类型校验
- split_text_by_marker函数
**作用**: 按照指定字符串 marker 分割文本,可选择是否保留 marker,并支持严格模式。
**参数**:
- text (str): 需要分割的原始文本。
- marker (str): 分割标记字符串。
- keep_marker (bool): 是否保留 marker 到结果中,默认为 True。
- strict (bool): 严格模式。当 marker 不存在时抛异常。默认为 False。
**返回**:
- List[str]: 分割后的文本列表。
如果 strict=False 且 marker 不存在,返回空列表。
如果 strict=True 且 marker 不存在,抛出 ValueError。
Raw data
{
"_id": null,
"home_page": null,
"name": "pycorelibs",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": "AILingues, components, core, library",
"author": null,
"author_email": "AI Lingues <support@ailingues.com>",
"download_url": null,
"platform": null,
"description": "# PyCoreLibs\n\nAI Lingues \u57fa\u7840\u7ec4\u4ef6\u5e93- PyCoreLibs\n\n## Message\n\n\u6d88\u606f\u5e93\uff0c\u57fa\u4e8eredis\u7684\u5f02\u6b65\u6d88\u606f\u6846\u67b6\n\n\u53c2\u8003:redis/README.md\n\n## Network\n\n### search \u641c\u7d22\u5f15\u64ce\n\n\u4e92\u8054\u7f51\u641c\u7d22\u5f15\u64ce\n\n### requests \u8bf7\u6c42\n\n- fetch_url \u5f3a\u5316\u7248\u901a\u7528\u7f51\u7edc\u8bf7\u6c42\u51fd\u6570\uff0c\u589e\u52a0\u4e86\u91cd\u8bd5\u673a\u5236,\u5e76\u9002\u7528\u4e8e\u590d\u6742\u573a\u666f\u3002\n\n- is_url \u9a8c\u8bc1\u662f\u5426\u4e3a\u5408\u6cd5url\n\n### scrawer \u7f51\u9875\u6587\u672c\u83b7\u53d6\n\n- HTMLContent \u63d0\u53d6\u7f51\u9875\u5185\u5bb9\n\n\n## security \u6a21\u5757\n\n### base64s \u6a21\u5757\n\n### cryptographys \u6a21\u5757\n\n----\n\n#### AsymmetricCrypto: \u7a33\u5065\u53ef\u9760\u7684\u975e\u5bf9\u79f0\u52a0\u89e3\u5bc6 + \u7b7e\u540d/\u9a8c\u7b7e \u5de5\u5177\u7c7b\n\n##### \u8bf4\u660e\n\n- \u52a0\u89e3\u5bc6\uff1aRSA-OAEP(SHA-256) + AES-GCM(256) \u7684\u6df7\u5408\u52a0\u5bc6\uff08\u4efb\u610f\u957f\u5ea6\u6570\u636e\uff09\n- \u7b7e\u540d\uff1aRSA-PSS(SHA-256)\uff0c\u8fd4\u56de JSON \u6253\u5305\uff08Base64\uff09\n- \u8bbe\u8ba1\u76ee\u6807\uff1a\u5065\u58ee\u3001\u8de8\u8bed\u8a00\u53cb\u597d\uff08JSON+Base64\uff09\u3001\u5f02\u5e38\u7edf\u4e00\u5c01\u88c5\n\n|\u51fd\u6570 |\u8bf4\u660e |\n|-------------------|------------------------------|\n|`generate_keypair` | \u751f\u6210\u5bc6\u94a5\u5bf9\uff08PEM\uff09 |\n|`encrypt` |\u516c\u94a5\u52a0\u5bc6\uff08\u6df7\u5408\u52a0\u5bc6\uff09 |\n|`decrypt` |\u79c1\u94a5\u89e3\u5bc6 |\n|`sign` |\u79c1\u94a5\u7b7e\u540d\uff08RSA-PSS + SHA-256\uff09 |\n|`verify` |\u516c\u94a5\u9a8c\u7b7e |\n\n##### \u793a\u4f8b\n\n ```python\n from pycorelibs.security.cryptographys import AsymmetricCrypto\n message = b\"hello, asymmetric crypto! \\xf0\\x9f\\x94\\x90\"\n\n # 1) \u751f\u6210\u5bc6\u94a5\u5bf9\uff08\u5185\u5b58\uff09\n priv, pub = AsymmetricCrypto.generate_keypair(\n key_size=2048, private_password=b\"pass\"\n )\n\n # 2) \u52a0\u5bc6\uff08\u53ef\u9009 AAD\uff09\n aad = b\"meta:example\"\n blob = AsymmetricCrypto.encrypt(message, pub, aad=aad)\n\n # 3) \u89e3\u5bc6\n plain = AsymmetricCrypto.decrypt(blob, priv, private_password=b\"pass\")\n assert plain == message\n print(\"Decrypt OK:\", plain.decode(\"utf-8\"))\n\n # 4) \u7b7e\u540d\n sig_blob = AsymmetricCrypto.sign(message, priv, private_password=b\"pass\")\n\n # 5) \u9a8c\u7b7e\uff08\u6210\u529f\uff09\n ok = AsymmetricCrypto.verify(message, sig_blob, pub)\n print(\"Verify OK:\", ok)\n\n # 6) \u9a8c\u7b7e\uff08\u5931\u8d25\u793a\u4f8b\uff1a\u6570\u636e\u88ab\u6539\u52a8\uff09\n tampered = message + b\"!\"\n bad = AsymmetricCrypto.verify(tampered, sig_blob, pub)\n print(\"Verify with tamper:\", bad)\n\n ```\n\n----\n\n##### generate_keypair\n\n\u751f\u6210 RSA \u5bc6\u94a5\u5bf9\uff08PKCS#8 \u79c1\u94a5 + X.509 \u516c\u94a5\uff0cPEM \u683c\u5f0f\uff09\n\n###### \u53c2\u6570\u8bf4\u660e\n\n- key_size (int, optional)\n\n 2048/3072/4096\uff08\u63a8\u8350 2048+\uff09. Defaults to 2048.\n\n- private_password (Optional[bytes], optional)\n\n \u7ed9\u79c1\u94a5\u52a0\u5bc6\u7684\u53e3\u4ee4\uff08None \u8868\u793a\u4e0d\u52a0\u5bc6\uff09. Defaults to None.\n\n- save_to (Optional[str], optional)\n\n \u53ef\u9009\u4fdd\u5b58\u76ee\u5f55\uff08\u5199\u5165 private.pem / public.pem\uff09. Defaults to None.\n\n###### Raises\n\n- ValueError:\n RSA key_size \u4ec5\u652f\u6301 2048/3072/4096\u3002\n\n- AsymmetricCryptoError:\n \u751f\u6210\u5bc6\u94a5\u5bf9\u5931\u8d25\n\n###### Returns\n\n- Tuple[bytes, bytes]\n \n \u751f\u6210 RSA \u5bc6\u94a5\u5bf9\uff08PKCS#8 \u79c1\u94a5 + X.509 \u516c\u94a5\uff0cPEM \u683c\u5f0f\uff09\n\n----\n\n### fingerprints \u6a21\u5757\n\n \u8de8\u5e73\u53f0\u786c\u4ef6\u6307\u7eb9\n\n- \u53ea\u7528 Python \u5e93\uff0c\u4e0d\u8d70\u7cfb\u7edf\u547d\u4ee4\uff1b\u4fe1\u606f\u66f4\u7a33\u5b9a\n- \u591a\u7f51\u5361\uff1a\u679a\u4e3e\u5168\u90e8\uff0c\u5e26\u8fc7\u6ee4\u9009\u9879\uff08\u662f\u5426\u5305\u542b\u865a\u62df/\u56de\u73af\u3001\u662f\u5426\u53ea\u53d6UP\u3001\u662f\u5426\u8981\u6c42\u6709IP\uff09\n- \u4e3b MAC \u9009\u62e9\u7b56\u7565\u53ef\u914d\u7f6e\n- \u751f\u6210\u54c8\u5e0c\u6307\u7eb9\uff08\u53ef\u4f20\u5165 salt \u63d0\u9ad8\u4f2a\u9020\u6210\u672c\uff09\n\n#### generate_hardware_fingerprint\n\n\u751f\u6210\u786c\u4ef6\u6307\u7eb9\uff08\u4e0d\u5305\u542bTPM\uff09\n\n##### \u53c2\u6570\u8bf4\u660e\n\n- isGPU (bool):\n\n \u662f\u5426\u83b7\u53d6GPU\u4fe1\u606f\uff0c\u7f3a\u7701\u4e3aFalse\n\n- salt (Optional[str], optional):\n\n \u989d\u5916\u76d0\u503c\uff08\u5b57\u7b26\u4e32\uff09\u3002\u5982\u7528\u4e8e\u6388\u6743/\u53cd\u722c\uff0c\u5efa\u8bae\u4f20\u5165\uff0c\u63d0\u9ad8\u4f2a\u9020\u6210\u672c\u3002. Defaults to None.\n\n##### Returns\n\nDict[str, Any]: {\n \"fid\": \"\\<sha512 hex\\>\",\n \"hash\": \"sha512\",\n \"input\": data,\n }\n\n- \"fid\": \"\\<sha512 hex\\>\"\n\n \u6700\u7ec8\u6307\u7eb9ID\uff0c\u957f\u5ea6128\u4e2a\u5b57\u7b26\n\n- \"hash\": \"sha512\"\n\n \u56fa\u5b9a\u6807\u8bb0\uff0c\u52a0\u5bc6\u7b97\u6cd5\u540d\n\n- input\": data\n \n \u52a0\u5bc6\u539f\u59cb\u6570\u636e\n\n##### \u793a\u4f8b\n\n ```python\n from pycorelibs.security.fingerprints import generate_hardware_fingerprint\n # \u5178\u578b\uff1a\u53ea\u8981UP\u3001\u6392\u9664\u865a\u62df\u548c\u56de\u73af\uff1b\u65e0IP\u4e5f\u53ef\uff08\u79bb\u7ebf\u673a\u5668\u4e5f\u80fd\u7ed9\u51fa\u6307\u7eb9\uff09\n fp = generate_hardware_fingerprint()\n print(fp)\n print(\"*\" * 80)\n # \u66f4\u201c\u8054\u7f51\u8bed\u4e49\u201d\uff1a\u8981\u6c42\u63a5\u53e3\u5fc5\u987b\u6709\u975e\u56de\u73afIP\n fp_net = generate_hardware_fingerprint(require_ip=True)\n print(fp_net)\n print(\"*\" * 80)\n # \u6709\u6388\u6743/\u53cd\u722c\u9700\u6c42\uff1a\u52a0\u76d0\n fp_salted = generate_hardware_fingerprint(salt=\"your-secret-salt-123\")\n print(fp_salted)\n print(\"*\" * 80)\n\n ```\n\n### md5s \u6a21\u5757\n\n#### md5\n\n\u8ba1\u7b97\u8f93\u5165\u5185\u5bb9\u7684 MD5 \u503c\n\n##### \u53c2\u6570\u8bf4\u660e\n\n- text (Any):\n \n \u4efb\u610f\u53ef\u8f6c\u4e3a\u5b57\u7b26\u4e32\u7684\u8f93\u5165\u5185\u5bb9\u3002\n\n- length (int):\n\n \u7ed3\u679c\u957f\u5ea6\uff0c\u53ef\u9009 16 \u6216 32\uff08\u9ed8\u8ba4 32\uff09\u3002\n\n- isHex (bool):\n\n \u662f\u5426\u4ee5\u5341\u516d\u8fdb\u5236\u683c\u5f0f\u8f93\u51fa\uff08\u9ed8\u8ba4\u4e3a True\uff09\u3002\n\n##### \u8fd4\u56de\n\n- (bool, Optional[str])\n\n \u6210\u529f\u4e0e\u5426\u3001\u4ee5\u53ca MD5 \u503c\u5b57\u7b26\u4e32\u6216 None\u3002\n\n----\n\n## system\u6a21\u5757\n\n\u83b7\u53d6\u5f53\u524d\u7cfb\u7edf\u4fe1\u606f\n\n- OS \u64cd\u4f5c\u7cfb\u7edf\n- CPU\n- Macs \u7f51\u5361\n- MB \u4e3b\u677f\n\n### runtime \u8fd0\u884c\u671f\u4fe1\u606f\u6a21\u5757\n\n \u83b7\u53d6\u8fd0\u884c\u671f\u76f8\u5173\u4fe1\u606f\n\n#### RunTimeInfo\u7c7b\n\n##### get_boot_time\n\n\u83b7\u53d6\u7cfb\u7edf\u542f\u52a8\u65f6\u95f4\u548c\u7cfb\u7edf\u5df2\u8fd0\u884c\u65f6\u957f\n\n\u8de8\u5e73\u53f0\u7ec6\u8282\uff1a\n\n- Linux\uff1aboot_time \u57fa\u672c\u6765\u81ea btime\uff08\u81ea\u542f\u52a8\u4ee5\u6765\u7684\u6302\u949f\u5dee\uff0c\u5305\u542b\u7761\u7720/\u4f11\u7720\u7684\u505c\u7559\u65f6\u95f4\uff09\u3002\n- Windows\uff1apsutil \u5185\u90e8\u901a\u5e38\u7528\u7cfb\u7edf tick \u4f30\u7b97\uff0c\u4e00\u822c\u4e0d\u5305\u542b\u7761\u7720\u65f6\u95f4\uff08\u66f4\u63a5\u8fd1\u201c\u6d3b\u8dc3\u8fd0\u884c\u65f6\u957f\u201d\uff09\u3002\n\n###### \u53c2\u6570\u8bf4\u660e\n\n- fmt (Optional[str], optional):\n \n \u65f6\u95f4\u683c\u5f0f\u5316\u5b57\u7b26\u4e32\uff0c\u9ed8\u8ba4\u4e3a None \u4f7f\u7528\u5f53\u524d\u7cfb\u7edf\u8bbe\u5b9a\u65f6\u533a\u7684\u672c\u5730\u683c\u5f0f.\n\n###### Returns\n\n- Dict[str, Any]:\n \u5305\u542b\u542f\u52a8\u65f6\u95f4\u6233\u3001\u683c\u5f0f\u5316\u65f6\u95f4\u3001\u5df2\u8fd0\u884c\u79d2\u6570\u548c\u53ef\u8bfb\u683c\u5f0f\n\n----\n\n###### get_uptime\n\n\u83b7\u53d6\u81ea start_mono\uff08monotonic \u8d77\u70b9\uff09\u81f3\u4eca\u7684\u8fd0\u884c\u65f6\u957f\uff08\u4e0d\u53d7\u7cfb\u7edf\u65f6\u949f\u8df3\u53d8\u5f71\u54cd\uff0c\u5982\u88ab NTP/\u624b\u52a8\u8c03\u6574\uff0c\u6216\u590f\u4ee4\u65f6\u5207\u6362\uff09\n\n###### \u53c2\u6570\u8bf4\u660e\n\n- start_mono (float, optional):\n\n \u8ba1\u7b97\u5f00\u59cb\u65f6\u95f4. Defaults to _SERVICE_START_.\n\n- fmt (Optional[str], optional)\uff1a\n\n \u65f6\u95f4\u683c\u5f0f\u5316\u5b57\u7b26\u4e32\uff0c\u9ed8\u8ba4\u4e3a None \u4f7f\u7528\u5f53\u524d\u7cfb\u7edf\u8bbe\u5b9a\u65f6\u533a\u7684\u672c\u5730\u683c\u5f0f.. Defaults to None.\n\n###### Returns\n\n- Dict[str, Any]:\n \n \u5305\u542b\u542f\u52a8\u65f6\u95f4\u6233\u3001\u683c\u5f0f\u5316\u65f6\u95f4\u3001\u5df2\u8fd0\u884c\u79d2\u6570\u548c\u53ef\u8bfb\u683c\u5f0f\n\n----\n\n## Utils\n\n----\n\n### config\u914d\u7f6e\u6a21\u5757\n\n\u901a\u7528\u7684\u672c\u5730\u914d\u7f6e\u5b58\u53d6\u5c42\uff08\u4e0e\u6570\u636e\u7ed3\u6784\u65e0\u5173\uff09\n\n 1. \u9762\u5411\u4efb\u610fPython\u5bf9\u8c61\uff1a\u901a\u8fc7\u53ef\u63d2\u62d4 Serializer \u9002\u914d\uff08JSON/YAML/TOML/Pickle/\u81ea\u5b9a\u4e49\u5747\u53ef\uff09\n 2. \u539f\u5b50\u5199\u5165\u3001\u81ea\u52a8\u521b\u5efa\u76ee\u5f55\u3001\u53ef\u9009\u81ea\u52a8\u4fdd\u5b58\n 3. \u591a\u547d\u540d\u7a7a\u95f4\uff1a\u5404\u81ea\u6587\u4ef6\u3001\u5404\u81ea\u5e8f\u5217\u5316\u5668\uff0c\u4e92\u4e0d\u5e72\u6270\n 4. \u9ed8\u8ba4\u4fdd\u5b58\u5230\u201c\u5e94\u7528\u540c\u76ee\u5f55\u201d\n 5. \u652f\u6301\u914d\u7f6e\u6587\u4ef6\u52a0\u5bc6\u5b58\u50a8\n\n#### ConfigRepo\u7c7b\n\n\u4ed3\u5e93\uff1a\u7ba1\u7406\u591a\u4e2a\u547d\u540d\u7a7a\u95f4\u3002\n\n \u4e00\u4e2a\u5e94\u7528\u4e00\u4e2a\u4ed3\u5e93\uff0c\u91cc\u9762\u53ef\u6ce8\u518c\u591a\u4e2a\u547d\u540d\u7a7a\u95f4\u3002\n\n \u6bcf\u4e2a\u547d\u540d\u7a7a\u95f4\uff1a\u72ec\u7acb\u6587\u4ef6 + \u72ec\u7acb\u5e8f\u5217\u5316\u5668 + \u72ec\u7acb\u9ed8\u8ba4\u503c\n\n##### \u6784\u9020\u51fd\u6570\n\n ```python\n __init__(self, root: Optional[str] = None, subdir: Optional[str] = None)\n ```\n\n**\u4f5c\u7528**\uff1a\u521b\u5efa\u4e00\u4e2a\u914d\u7f6e\u4ed3\u5e93\u5b9e\u4f8b\uff0c\u786e\u5b9a\u6240\u6709\u547d\u540d\u7a7a\u95f4\u6587\u4ef6\u7684\u6839\u76ee\u5f55\u3002\n\n###### \u53c2\u6570\u8bf4\u660e\n\n- root\uff1astr | None\n\n \u4ed3\u5e93\u6839\u76ee\u5f55\u7684\u7edd\u5bf9/\u76f8\u5bf9\u8def\u5f84\u3002\u82e5\u4e3a None\uff0c\u5219\u81ea\u52a8\u89e3\u6790\u4e3a**\u5e94\u7528\u540c\u76ee\u5f55**\uff1a\n\n - PyInstaller \u6253\u5305\u540e\uff1aos.path.dirname(sys.executable)\n\n - \u666e\u901a\u811a\u672c\uff1aos.path.dirname(os.path.abspath(sys.argv[0] or __file__))\n\n- subdir\uff1astr | None\n\n root \u4e0b\u7684\u5b50\u76ee\u5f55\u540d\uff08\u4f8b\u5982 \"config\"\uff09\uff0c\u7528\u4e8e\u628a\u914d\u7f6e\u96c6\u4e2d\u5230 root/subdir\u3002\n\n###### \u8fd4\u56de\n\n\u65e0\u3002\n\n###### \u526f\u4f5c\u7528\n\n\u82e5\u76ee\u5f55\u4e0d\u5b58\u5728\u4f1a\u81ea\u52a8\u521b\u5efa\u3002\n\n###### \u793a\u4f8b\n\n ```python\n\n # \u4fdd\u5b58\u5230\u5e94\u7528\u76ee\u5f55\u4e0b\u7684 ./config/\n repo = ConfigRepo(subdir=\"config\")\n\n # \u4fdd\u5b58\u5230\u6307\u5b9a\u7edd\u5bf9\u8def\u5f84\n repo = ConfigRepo(root=\"D:/myapp/config\")\n ```\n\n- register\u6ce8\u518c\u547d\u540d\u7a7a\u95f4\n ```python\n register(self, name: str, serializer: Serializer[Any], filename: Optional[str] = None, default_factory: Optional[Callable[[], Any]] = None, autosave: bool = False, backup_on_save: bool = False, cipher: Optional[AesGcmBox] = None, cipher_policy: str = \"auto\", encrypt_on_save_if_cipher: bool = True) -> Namespace[Any]\n ```\n\n **\u4f5c\u7528**\uff1a\n\n \u6ce8\u518c\u4e00\u4e2a\u547d\u540d\u7a7a\u95f4\uff08\u5373\u4e00\u4e2a\u903b\u8f91\u201c\u914d\u7f6e\u7a7a\u95f4\u201d\uff09\uff0c\u7ed1\u5b9a\u6587\u4ef6\u3001\u5e8f\u5217\u5316\u5668\u3001\u9ed8\u8ba4\u503c\u4e0e\u52a0\u5bc6\u7b56\u7565\u3002\n\n **\u53c2\u6570**\uff1a\n\n - name\uff1astr\n \u547d\u540d\u7a7a\u95f4\u540d\u79f0\uff08\u5728\u4ed3\u5e93\u5185\u552f\u4e00\uff09\u3002\n\n - serializer\uff1aSerializer[Any]\n \u5e8f\u5217\u5316\u5668\u5b9e\u4f8b\uff1a\u5982 JsonSerializer()\u3001PickleSerializer() \u6216\u81ea\u5b9a\u4e49\u5b9e\u73b0\uff08\u9700\u63d0\u4f9b dumps(obj)->bytes \u4e0e loads(bytes)->obj\uff09\u3002\n\n - filename\uff1astr | None\n \u6587\u4ef6\u540d\u3002\u82e5\u7701\u7565\uff1a\u5f53 serializer \u4e3a JsonSerializer \u65f6\u9ed8\u8ba4 \"<name>.json\"\uff0c\u5426\u5219\u4e3a \"<name>.bin\"\u3002\n\n - default_factory\uff1a() -> Any | None\n \u5f53\u6587\u4ef6\u4e0d\u5b58\u5728\u6216\u9700\u91cd\u7f6e\u65f6\uff0c\u7528\u6b64\u51fd\u6570\u60f0\u6027\u751f\u6210\u9ed8\u8ba4\u5bf9\u8c61\uff08\u5982 dict/list/\u81ea\u5b9a\u4e49\u5bf9\u8c61\uff09\u3002\u82e5\u7701\u7565\uff0c\u9ed8\u8ba4\u503c\u4e3a None\u3002\n\n - autosave\uff1abool\n \u8bbe\u4e3a True \u65f6\uff0c\u8c03\u7528 set() / update() / reset() \u7b49\u4fee\u6539\u540e\u4f1a\u81ea\u52a8\u4fdd\u5b58\u5230\u6587\u4ef6\u3002\n\n - backup_on_save\uff1abool\n \u4fdd\u5b58\u524d\u82e5\u76ee\u6807\u6587\u4ef6\u5df2\u5b58\u5728\uff0c\u5c06\u5176\u91cd\u547d\u540d\u4e3a *.bak \u4f5c\u4e3a\u5907\u4efd\u3002\n\n - cipher\uff1aAesGcmBox | None\n \u53ef\u9009\u7684\u52a0\u5bc6\u76d2\uff1b\u63d0\u4f9b\u5219\u8fdb\u884c\u52a0\u5bc6\u5b58\u50a8\uff08AES-GCM\uff09\u3002\u4e0d\u63d0\u4f9b\u5219\u660e\u6587\u5b58\u50a8\u3002\n\n - cipher_policy\uff1a\"auto\" | \"require\"\uff08\u9ed8\u8ba4 \"auto\"\uff09\n\n - \"auto\"\uff1a\u8bfb\u53d6\u65f6\u82e5\u68c0\u6d4b\u5230\u52a0\u5bc6\u9b54\u6570\u5219\u89e3\u5bc6\uff0c\u5426\u5219\u6309\u660e\u6587\u52a0\u8f7d\uff08\u517c\u5bb9\u65e7\u6587\u4ef6\uff09\uff1b\n\n - \"require\"\uff1a\u5f3a\u5236\u8981\u6c42\u6587\u4ef6\u4e3a\u52a0\u5bc6\u683c\u5f0f\uff0c\u82e5\u65e0\u9b54\u6570\u76f4\u63a5\u629b\u9519\uff08\u9002\u5408\u654f\u611f\u7a7a\u95f4\uff09\u3002\n\n - encrypt_on_save_if_cipher\uff1abool\uff08\u9ed8\u8ba4 True\uff09\n \u5f53 cipher \u5b58\u5728\u4e14\u6b64\u524d\u662f\u660e\u6587\u52a0\u8f7d\u7684\u65e7\u6587\u4ef6\uff0c\u4fdd\u5b58\u65f6\u4f1a\u81ea\u52a8\u8fc1\u79fb\u4e3a\u52a0\u5bc6\u683c\u5f0f\u3002\n\n **\u8fd4\u56de**\uff1a\n\n Namespace[Any]\uff1a\u5df2\u6ce8\u518c\u7684\u547d\u540d\u7a7a\u95f4\u5bf9\u8c61\u3002\n\n **\u5f02\u5e38**\uff1a\n \n \u65e0\uff08\u4f46\u540e\u7eed\u52a0\u8f7d/\u4fdd\u5b58\u65f6\u53ef\u80fd\u629b\u51fa\u5e8f\u5217\u5316/IO/\u89e3\u5bc6\u7c7b\u5f02\u5e38\uff09\u3002\n\n **\u793a\u4f8b**\uff1a\n ```python\n # \u660e\u6587 JSON \u504f\u597d\u8bbe\u7f6e\n ui = repo.register(\n name=\"ui\",\n serializer=JsonSerializer(),\n filename=\"ui.json\",\n default_factory=lambda: {\"theme\": \"light\", \"recent_files\": []},\n autosave=True\n )\n\n # \u52a0\u5bc6\u7684 Pickle \u7f13\u5b58\uff08\u65e7\u660e\u6587\u6587\u4ef6\u53ef\u517c\u5bb9\u8bfb\u53d6\uff0c\u4fdd\u5b58\u540e\u81ea\u52a8\u8fc1\u79fb\u4e3a\u5bc6\u6587\uff09\n cache = repo.register(\n name=\"cache\",\n serializer=PickleSerializer(),\n filename=\"cache.dat\",\n default_factory=list,\n autosave=True,\n cipher=AesGcmBox(password=os.environ[\"CACHE_PASSWORD\"]),\n cipher_policy=\"auto\",\n encrypt_on_save_if_cipher=True,\n )\n ```\n ----\n\n- space \u53d6\u547d\u540d\u7a7a\u95f4\u5bf9\u8c61\n ```python\n space(self, name: str) -> Namespace[Any]\n ```\n **\u4f5c\u7528**\uff1a\u6309\u540d\u79f0\u83b7\u53d6\u5df2\u6ce8\u518c\u7684\u547d\u540d\u7a7a\u95f4\u5bf9\u8c61\u3002\n\n **\u53c2\u6570**\uff1a\n\n - name\uff1astr\n \u6ce8\u518c\u65f6\u4f7f\u7528\u7684\u547d\u540d\u7a7a\u95f4\u540d\u79f0\u3002\n\n **\u8fd4\u56de**\uff1a\n\n - Namespace[Any]\uff1a\u547d\u540d\u7a7a\u95f4\u5bf9\u8c61\u3002\n\n **\u5f02\u5e38**\uff1a\n\n - KeyError\uff1a\u5f53 name \u5c1a\u672a\u6ce8\u518c\u65f6\u629b\u51fa\u3002\n\n **\u793a\u4f8b**\uff1a\n\n ```python\n cache_ns = repo.space(\"cache\")\n ```\n ----\n\n- get\u76f4\u63a5\u5b58\u53d6\u5bf9\u8c61\uff08\u4fbf\u6377\u65b9\u6cd5\uff09\n\n ```python\n get(self, name: str) -> Any\n ```\n\n **\u4f5c\u7528**\uff1a\u4ece\u6307\u5b9a\u547d\u540d\u7a7a\u95f4\u8bfb\u53d6\u5bf9\u8c61\uff08\u5185\u90e8\u82e5\u672a\u52a0\u8f7d\u4f1a\u81ea\u52a8\u8c03\u7528 Namespace.load()\uff09\u3002\n\n **\u53c2\u6570**\uff1a\n\n - name\uff1astr\n\n **\u8fd4\u56de**\uff1a\n\n - Any\uff1a\u547d\u540d\u7a7a\u95f4\u4e2d\u7684\u5bf9\u8c61\uff08\u53ef\u80fd\u662f dict/list/\u81ea\u5b9a\u4e49\u5bf9\u8c61/None \u7b49\uff09\u3002\n\n **\u5f02\u5e38**\uff1a\n\n - KeyError\uff1a\u547d\u540d\u7a7a\u95f4\u672a\u6ce8\u518c\uff1b\n\n - \u53cd\u5e8f\u5217\u5316/\u89e3\u5bc6/IO \u5f02\u5e38\uff1a\u5e95\u5c42\u629b\u51fa\u7684 ValueError\u3001OSError \u7b49\u3002\n\n **\u793a\u4f8b**\uff1a\n ```python\n ui_data = repo.get(\"ui\") # \u4f8b\u5982: {\"theme\": \"light\", \"recent_files\": []}\n ```\n ----\n\n- set(self, name: str, value: Any, save: Optional[bool] = None) -> None\n\n **\u4f5c\u7528**\uff1a\u8bbe\u7f6e\u547d\u540d\u7a7a\u95f4\u5bf9\u8c61\u5e76\u53ef\u9009\u62e9\u4fdd\u5b58\u3002\n\n **\u53c2\u6570**\uff1a\n\n - name\uff1astr\n\n - value\uff1aAny\n \u8981\u5199\u5165\u7684\u5bf9\u8c61\uff08\u4f1a\u901a\u8fc7\u7ed1\u5b9a\u7684 serializer \u6301\u4e45\u5316\uff09\u3002\n\n - save\uff1abool | None\n\n **True**\uff1a\u7acb\u5373\u4fdd\u5b58\uff1b\n\n **False**\uff1a\u53ea\u66f4\u65b0\u5185\u5b58\u7f13\u5b58\uff0c\u4e0d\u843d\u76d8\uff1b\n\n **None**\uff1a\u8ddf\u968f\u547d\u540d\u7a7a\u95f4\u7684 autosave \u8bbe\u7f6e\uff08\u82e5\u4e3a True \u5219\u4fdd\u5b58\uff09\u3002\n\n **\u8fd4\u56de**\uff1a\u65e0\u3002\n\n **\u5f02\u5e38**\uff1a\u540c get()\u3002\n\n **\u793a\u4f8b**\uff1a\n ```python\n repo.set(\"ui\", {\"theme\": \"dark\"}, save=True)\n ```\n ----\n\n- update(self, name: str, mutator: Callable[[Any], Any], save: Optional[bool] = None) -> Any\n\n **\u4f5c\u7528**\uff1a\u5728\u539f\u5bf9\u8c61\u57fa\u7840\u4e0a\u4ee5\u51fd\u6570\u5f0f\u65b9\u5f0f\u53d8\u6362\u5e76\u56de\u5199\uff08\u8bfb -> \u53d8\u6362 -> \u5199\uff09\u3002\n\n **\u53c2\u6570**\uff1a\n\n - name\uff1astr\n\n - mutator\uff1a(obj: Any) -> Any\n \u5165\u53c2\u662f\u5f53\u524d\u5bf9\u8c61\uff0c\u8fd4\u56de\u53d8\u6362\u540e\u7684\u65b0\u5bf9\u8c61\uff08\u51fd\u6570\u5185\u5e94\u8fd4\u56de\u65b0\u503c\uff09\u3002\n\n - save\uff1abool | None\n \u8bed\u4e49\u540c set() \u7684 save\u3002\n\n **\u8fd4\u56de**\uff1a\n\n - Any\uff1a\u53d8\u6362\u540e\u7684\u65b0\u5bf9\u8c61\u3002\n\n **\u5f02\u5e38**\uff1a\u540c get()/set()\u3002\n\n **\u793a\u4f8b**\uff1a\n ```python\n def add_recent(d):\n d = d or {}\n rec = (d.get(\"recent_files\") or []) + [\"a.sdf\"]\n return {**d, \"recent_files\": rec[-10:]} # \u53ea\u4fdd\u7559\u6700\u8fd110\u6761\n\n new_ui = repo.update(\"ui\", add_recent, save=True)\n ```\n ----\n\n- \u6279\u91cf\u64cd\u4f5c\nsave_all(self) -> None\n\n **\u4f5c\u7528**\uff1a\u5bf9\u6240\u6709\u201c\u5df2\u52a0\u8f7d\u8fc7\u201d\u7684\u547d\u540d\u7a7a\u95f4\u6267\u884c save()\uff08\u907f\u514d\u65e0\u610f\u4e49\u7684\u7a7a\u5199\uff09\u3002\n\n **\u53c2\u6570**\uff1a\u65e0\u3002\n\n **\u8fd4\u56de**\uff1a\u65e0\u3002\n\n **\u5f02\u5e38**\uff1a\u5e95\u5c42\u4fdd\u5b58\u53ef\u80fd\u629b\u51fa IO/\u5e8f\u5217\u5316/\u52a0\u5bc6\u76f8\u5173\u5f02\u5e38\u3002\n\n \u6ce8\u610f\uff1a\n\n \u4ec5\u5bf9\u5185\u90e8\u6807\u8bb0 _loaded == True \u7684\u7a7a\u95f4\u751f\u6548\uff1b\n\n \u82e5\u4f60\u9700\u8981\u786e\u4fdd\u6240\u6709\u7a7a\u95f4\u90fd\u6709\u521d\u503c\u5e76\u4fdd\u5b58\uff0c\u53ef\u5148 load_all() \u6216\u9010\u4e2a space(...).get() \u6fc0\u6d3b\u3002\n\n **\u793a\u4f8b**\uff1a\n ```python\n # \u67d0\u4e9b\u7a7a\u95f4 autosave=False\uff0c\u96c6\u4e2d\u4e00\u6b21\u6027\u4fdd\u5b58\n repo.save_all()\n ```\n ----\n\n- load_all(self) -> None\n\n **\u4f5c\u7528**\uff1a\u5bf9\u6240\u6709\u5df2\u6ce8\u518c\u7684\u547d\u540d\u7a7a\u95f4\u6267\u884c load()\uff08\u5c06\u6587\u4ef6\u5185\u5bb9\u52a0\u8f7d\u5230\u5185\u5b58\u7f13\u5b58\uff09\u3002\n\n **\u53c2\u6570**\uff1a\u65e0\u3002\n\n **\u8fd4\u56de**\uff1a\u65e0\u3002\n\n **\u5f02\u5e38**\uff1a\u5e95\u5c42\u52a0\u8f7d\u53ef\u80fd\u629b\u51fa IO/\u53cd\u5e8f\u5217\u5316/\u89e3\u5bc6\u76f8\u5173\u5f02\u5e38\u3002\n\n **\u793a\u4f8b**\uff1a\n\n ```python\n # \u5e94\u7528\u542f\u52a8\u65f6\u9884\u70ed\u5168\u90e8\u914d\u7f6e\n repo.load_all()\n ```\n\n#### \u5e38\u89c1\u95ee\u9898\u4e0e\u6ce8\u610f\u4e8b\u9879\n\n- \u8def\u5f84\u9009\u62e9\uff1a\n\n \u9ed8\u8ba4\u843d\u5728\u5e94\u7528\u540c\u76ee\u5f55\uff0c\u4fbf\u4e8e\u201c\u5c31\u5730\u643a\u5e26\u201d\u3002\u5982\u9700\u8de8\u7528\u6237\u5171\u4eab\u6216\u5199\u6743\u9650\u53d7\u9650\uff08Windows Program Files\uff09\uff0c\u5efa\u8bae\u663e\u5f0f\u6307\u5b9a root\u3002\n\n- \u9ed8\u8ba4\u503c\uff1a\n\n default_factory \u63a8\u8350\u4f20\u5165 \u53ef\u8c03\u7528\uff08\u5982 dict/list/lambda\uff09\uff0c\u907f\u514d\u53ef\u53d8\u5bf9\u8c61\u4f5c\u4e3a\u9ed8\u8ba4\u53c2\u6570\u5e26\u6765\u7684\u5171\u4eab\u526f\u4f5c\u7528\u3002\n\n- \u52a0\u5bc6\u7b56\u7565\uff1a\n\n - **\"auto\"**\uff1a\u9002\u5408\u4ece\u65e7\u660e\u6587\u5e73\u6ed1\u8fc1\u79fb\u4e3a\u5bc6\u6587\uff1b\n\n - **\"require\"**\uff1a\u9002\u5408\u5f3a\u7ea6\u675f\u7684\u654f\u611f\u7a7a\u95f4\uff08\u82e5\u975e\u52a0\u5bc6\u7acb\u523b\u62a5\u9519\uff09\u3002\n\n- \u5907\u4efd\u6587\u4ef6\uff1a\n\n backup_on_save=True \u65f6\u4f1a\u751f\u6210 *.bak\uff1b\u5176\u5185\u5bb9\u53ef\u80fd\u4e3a\u4e0a\u4e00\u6b21\u4fdd\u5b58\u65f6\u7684\u660e\u6587\u6216\u5bc6\u6587\uff0c\u8bfb\u53d6\u65f6\u53ef\u7528 AesGcmBox.is_encrypted() \u5224\u522b\u3002\n\n- \u5f02\u5e38\u5904\u7406\uff1a\n\n \u89e3\u5bc6\u5931\u8d25\u901a\u5e38\u662f\u53e3\u4ee4/\u5bc6\u94a5\u4e0d\u5339\u914d\u6216\u6587\u4ef6\u635f\u574f\uff1b\u8bf7\u6355\u83b7 ValueError \u5e76\u5f15\u5bfc\u7528\u6237\u68c0\u67e5\u53e3\u4ee4\u6765\u6e90\u3002\n\n- \u7ebf\u7a0b/\u8fdb\u7a0b\u5e76\u53d1\uff1a\n \n \u5f53\u524d\u5b9e\u73b0\u662f\u5355\u8fdb\u7a0b\u5185\u4f7f\u7528\u7684\u7b80\u5355\u672c\u5730\u5c42\uff1b\u82e5\u5b58\u5728\u591a\u8fdb\u7a0b/\u591a\u5b9e\u4f8b\u540c\u65f6\u5199\u5165\u540c\u4e00\u6587\u4ef6\uff0c\u8bf7\u81ea\u884c\u52a0\u9501\u6216\u91c7\u7528\u66f4\u9ad8\u7ea7\u522b\u7684\u5b58\u50a8\u65b9\u6848\u3002\n\n#### \u5178\u578b\u7528\u6cd5\u793a\u4f8b\uff08\u6574\u5408\uff09\n```python\nfrom pycorelibs.utils.config import ConfigRepo, JsonSerializer, PickleSerializer\nfrom pycorelibs.security.aesgcmbox import AesGcmBox\nimport os\n\nrepo = ConfigRepo(subdir=\"config\")\n\n# \u660e\u6587 UI \u914d\u7f6e\nui = repo.register(\n name=\"ui\",\n serializer=JsonSerializer(),\n filename=\"ui.json\",\n default_factory=lambda: {\"theme\": \"light\", \"recent_files\": []},\n autosave=True,\n)\n\n# \u52a0\u5bc6\u7684\u7f13\u5b58\uff08\u4ece\u65e7\u660e\u6587\u81ea\u52a8\u8fc1\u79fb\uff09\ncache = repo.register(\n name=\"cache\",\n serializer=PickleSerializer(),\n filename=\"cache.dat\",\n default_factory=list,\n autosave=True,\n cipher=AesGcmBox(password=os.environ.get(\"CACHE_PW\", \"CHANGE_ME\")),\n cipher_policy=\"auto\",\n encrypt_on_save_if_cipher=True,\n)\n\n# \u8bfb\u53d6 / \u66f4\u65b0 / \u6279\u91cf\u4fdd\u5b58\nui_data = repo.get(\"ui\")\nrepo.update(\"ui\", lambda d: {**(d or {}), \"theme\": \"dark\"})\nrepo.update(\"cache\", lambda lst: (lst or []) + [\"session#1\"])\nrepo.save_all()\n\n```\n----\n\n### files\u6587\u4ef6\u5904\u7406\n\n- EnumFileSizeUnit\n\n- FilesInfo \u7c7b\n\n - get_file_size\n \u83b7\u53d6\u6587\u4ef6\u5927\u5c0f\uff0c\u5e76\u8fd4\u56de\u6307\u5b9a\u5355\u4f4d\u7684\u5927\u5c0f\u3002\n\n \u652f\u6301B,KB,MB,GB,TB\n\n - line_count\n\n \u83b7\u53d6\u6307\u5b9a\u6587\u4ef6\u7684\u603b\u884c\u6570\n\n Windows\u7cfb\u7edf: \u4f7f\u7528\u6362\u884c\u7b26\u4f5c\u4e3a\u5224\u65ad\u6807\u8bb0\n Linux\u7cfb\u7edf: \u4f7f\u7528\u7cfb\u7edf\u81ea\u5e26\u7684wc\u547d\u4ee4\u7edf\u8ba1\n\n \u6ce8: \u8fd1\u9002\u7528\u4e8e\u6587\u672c\u7c7b\u578b\u6587\u4ef6,\u5176\u4ed6\u7c7b\u578b\u5982\u4e8c\u8fdb\u5236\u6587\u4ef6\u7edf\u8ba1\u7ed3\u679c\u4e0d\u5177\u5907\u53c2\u8003\u610f\u4e49\n\n - get_directory_last_modified_time\n \u83b7\u53d6\u6307\u5b9a\u76ee\u5f55\u6700\u540e\u4fee\u6539\u65f6\u95f4\n\n - get_directory_last_modified_timestamp\n \u83b7\u53d6\u6307\u5b9a\u76ee\u5f55\u6700\u540e\u4fee\u6539\u65f6\u95f4\u6233\n\n### formate \u6570\u636e\u683c\u5f0f\u5316\u6307\u5b9a\u7c7b\u578b\n\nFormates \u7c7b\uff0c\u9759\u6001\u65b9\u6cd5\u5982\u4e0b\uff1a\n\n- fInt \u8f6c\u6362\u4e3a\u6574\u6570\n- fFloat \u8f6c\u6362\u4e3a\u6d6e\u70b9\u6570\n- fBool \u8f6c\u6362\u4e3a\u5e03\u5c14\u503c\n- fDate \u6309\u6307\u5b9a\u683c\u5f0f\u89e3\u6790\u65e5\u671f\n- fEnum \u6821\u9a8c\u503c\u662f\u5426\u5728\u5141\u8bb8\u96c6\u5408\u5185\uff08\u7528\u4e8e\u679a\u4e3e\u5b57\u6bb5\uff09\n- fJSON \u89e3\u6790\u4e3a JSON \u5bf9\u8c61\n\n### logger\u65e5\u5fd7\n\n- \u652f\u6301\u81ea\u52a8\u4ea7\u751f\u65e5\u5fd7\n- \u652f\u6301\u540c\u65f6\u5411\u63a7\u5236\u53f0\u548c\u65e5\u5fd7\u6587\u4ef6\u8f93\u51fa\n- \u652f\u6301\u8fdb\u5ea6\u6761\n- \u652f\u6301info\u3001warning\u548cerror\u4e09\u4e2a\u7ea7\u522b\u7684\u65e5\u5fd7\n\n### strings \u5b57\u7b26\u4e32\u5904\u7406\n\n- UniCodeGenerator \u6a21\u5757\n\n \u521b\u5efa\u968f\u673a\u7801\uff0c\u65e5\u671f\u524d\u7f00 + \u968f\u673a\u7801\n\n \u53ef\u914d\u7f6e\u3001\u4f7f\u7528 secrets \u751f\u6210\u9ad8\u5f3a\u5ea6\u968f\u673a\u7801\u7684\u7f16\u7801\u751f\u6210\u7c7b\n\n | \u573a\u666f | \u53c2\u6570\u793a\u4f8b |\n | ----------------| ---------------------------------------------- |\n | \u9080\u8bf7\u7801\uff08\u77ed\uff09 | `prefix_date=False, random_length=6` | \n | \u8ba2\u5355\u53f7\uff08\u5f53\u5929\u552f\u4e00\uff09| `prefix_date=True, random_length=4` |\n | URL \u5b89\u5168\u77ed\u7801 | `charset=string.ascii_letters + string.digits` |\n\n- json_dumps_bytes \u51fd\u6570\n\n \u5c06 dict \u5e8f\u5217\u5316\u4e3a\u7d27\u51d1 UTF-8 JSON bytes\n\n- json_loads_bytes \u51fd\u6570\n\n \u5c06 UTF-8 JSON bytes \u89e3\u6790\u4e3a dict\uff0c\u5e76\u8fdb\u884c\u57fa\u672c\u7c7b\u578b\u6821\u9a8c\n \n- split_text_by_marker\u51fd\u6570\n \n **\u4f5c\u7528**: \u6309\u7167\u6307\u5b9a\u5b57\u7b26\u4e32 marker \u5206\u5272\u6587\u672c\uff0c\u53ef\u9009\u62e9\u662f\u5426\u4fdd\u7559 marker\uff0c\u5e76\u652f\u6301\u4e25\u683c\u6a21\u5f0f\u3002\n\n **\u53c2\u6570**\uff1a\n - text (str): \u9700\u8981\u5206\u5272\u7684\u539f\u59cb\u6587\u672c\u3002\n - marker (str): \u5206\u5272\u6807\u8bb0\u5b57\u7b26\u4e32\u3002\n - keep_marker (bool): \u662f\u5426\u4fdd\u7559 marker \u5230\u7ed3\u679c\u4e2d\uff0c\u9ed8\u8ba4\u4e3a True\u3002\n - strict (bool): \u4e25\u683c\u6a21\u5f0f\u3002\u5f53 marker \u4e0d\u5b58\u5728\u65f6\u629b\u5f02\u5e38\u3002\u9ed8\u8ba4\u4e3a False\u3002\n\n **\u8fd4\u56de**\uff1a\n - List[str]: \u5206\u5272\u540e\u7684\u6587\u672c\u5217\u8868\u3002\n \u5982\u679c strict=False \u4e14 marker \u4e0d\u5b58\u5728\uff0c\u8fd4\u56de\u7a7a\u5217\u8868\u3002\n \u5982\u679c strict=True \u4e14 marker \u4e0d\u5b58\u5728\uff0c\u629b\u51fa ValueError\u3002\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A collection of reusable python core library from AI Lingues.",
"version": "0.2.7",
"project_urls": null,
"split_keywords": [
"ailingues",
" components",
" core",
" library"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "a115502fdb197f28bc025799797901e1a1a3a643435eb2b4a1e3bc9f34ca4b66",
"md5": "5aae032e111a0480caaf508f4950b19f",
"sha256": "5ee4ea2eccc09ac8ce48c15cf5ad7463c4c9ce34153a1157be7db3f1e81b3e69"
},
"downloads": -1,
"filename": "pycorelibs-0.2.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
"has_sig": false,
"md5_digest": "5aae032e111a0480caaf508f4950b19f",
"packagetype": "bdist_wheel",
"python_version": "cp311",
"requires_python": ">=3.11",
"size": 8703448,
"upload_time": "2025-10-23T04:52:54",
"upload_time_iso_8601": "2025-10-23T04:52:54.156575Z",
"url": "https://files.pythonhosted.org/packages/a1/15/502fdb197f28bc025799797901e1a1a3a643435eb2b4a1e3bc9f34ca4b66/pycorelibs-0.2.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "761a30922e6e38a6da68a4ae99f61d953abe5885e085b023ae3ec6abd8bff541",
"md5": "a84e8279919b28cd4d93bbe165d13341",
"sha256": "3b0b9e492cc4613ffa5031daef540fc013a10d5684035f9aec95ab3c4202c6be"
},
"downloads": -1,
"filename": "pycorelibs-0.2.7-cp311-cp311-win_amd64.whl",
"has_sig": false,
"md5_digest": "a84e8279919b28cd4d93bbe165d13341",
"packagetype": "bdist_wheel",
"python_version": "cp311",
"requires_python": ">=3.11",
"size": 3746239,
"upload_time": "2025-10-23T04:52:56",
"upload_time_iso_8601": "2025-10-23T04:52:56.209232Z",
"url": "https://files.pythonhosted.org/packages/76/1a/30922e6e38a6da68a4ae99f61d953abe5885e085b023ae3ec6abd8bff541/pycorelibs-0.2.7-cp311-cp311-win_amd64.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-23 04:52:54",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "pycorelibs"
}