Metadata-Version: 2.4
Name: xiaoe-config-manager
Version: 1.0.1
Summary: 多进程安全的 JSON 配置文件管理器，支持默认配置、动态重置与进程间自动同步
License-Expression: MIT
Project-URL: Homepage, https://github.com/xiaoe/xiaoe-config-manager
Project-URL: Repository, https://github.com/xiaoe/xiaoe-config-manager
Keywords: config,configuration,json,multiprocess,thread-safe
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# xiaoe-config-manager

多进程安全的 JSON 配置文件管理器，纯 Python 标准库实现，零外部依赖。

## 特性

- **线程安全** — 内置线程锁，单进程多线程环境下安全读写
- **多进程安全** — 通过 `multiprocessing.Queue` 双向管道实现主进程与子进程之间的配置自动同步
- **默认配置** — 支持静态默认字典和动态回调两种方式，配置项缺失时自动补齐
- **配置重置** — 支持单个 key 或全部配置一键恢复默认值
- **默认值热更新** — 当默认配置变化时，自动识别用户未修改的项并更新为新默认值
- **纯标准库** — 无任何第三方依赖，仅使用 `json`、`threading`、`multiprocessing` 等标准库

## 安装

```bash
pip install xiaoe-config-manager
```

## 快速开始

### 单进程使用

```python
from xiaoe_config_manager import Config

# 初始化，指定配置文件名（不含扩展名）
config = Config("app_config")

# 写入配置
config.configset("api_url", "https://api.example.com")
config.configset("timeout", 30)

# 读取配置
print(config.configget("api_url"))  # https://api.example.com

# 读取全部配置
all_config = config.configget("all")
print(all_config)  # {"api_url": "https://api.example.com", "timeout": 30}
```

### 带默认配置

```python
from xiaoe_config_manager import Config

defaults = {
    "host": "0.0.0.0",
    "port": 8080,
    "debug": False,
}

config = Config("server_config", default_config=defaults)

# 配置文件中已有的键保留原值，缺失的键自动用默认值补齐
print(config.configget("host"))   # "0.0.0.0"
print(config.configget("port"))   # 8080

# 恢复单个配置项为默认值
config.configreset("port")

# 恢复全部配置为默认值
config.configreset("all")
```

### 使用回调函数动态生成默认配置

```python
from xiaoe_config_manager import Config

def dynamic_defaults(current_config):
    """根据当前配置动态生成默认值"""
    import socket
    return {
        "host": "0.0.0.0",
        "port": 8080,
        "hostname": socket.gethostname(),
        "worker_count": 4,
    }

config = Config(
    "dynamic_config",
    default_config_callback=dynamic_defaults,
    init_use_callback=True,  # 初始化时调用回调
)

# 重置时会再次调用回调，获取最新的默认值
config.configreset("all")
```

## 多进程使用

多进程场景下，**主进程持有 `Config` 单例**，子进程通过 `ConfigInChildProcessPipe` 与主进程通信。主进程需要为每个子进程创建一个 `ConfigInMainProcessPipe` 实例。

### 主进程

```python
from xiaoe_config_manager import Config, ConfigInMainProcessPipe

# 1. 创建配置单例
config = Config("my_config", default_config={"workers": 4})

# 2. 为子进程创建主进程端的管道
main_pipe = ConfigInMainProcessPipe(config)

# 3. 获取队列，传递给子进程
set_queue, get_queue = main_pipe.get_queues()

# 4. 启动子进程（以 multiprocessing.Process 为例）
import multiprocessing

def worker(set_q, get_q):
    from xiaoe_config_manager import ConfigInChildProcessPipe
    # 5. 为子进程创建子进程端的管道
    child_config = ConfigInChildProcessPipe(set_q, get_q)
    child_config.configset("worker_status", "running")
    child_config.close()

p = multiprocessing.Process(target=worker, args=(set_queue, get_queue))
p.start()
p.join()

# 6. 主进程可直接读写配置，修改会自动同步并广播到到子进程。
# 同样，子进程的修改请求会提交至主进程修改，最后由主进程广播至所有子进程。
config.configset("master_status", "ok")

# 6. 清理
main_pipe.close()
```

### 子进程

```python
from xiaoe_config_manager import ConfigInChildProcessPipe

def worker(set_queue, get_queue):
    # 使用主进程传入的队列初始化
    config = ConfigInChildProcessPipe(set_queue, get_queue)

    # 读取配置（从本地缓存读取，无 I/O 开销）
    print(config.configget("workers"))  # 4

    # 修改配置（发送消息给主进程，由主进程执行实际写入）
    config.configset("worker_status", "done")

    # 主进程的修改会自动同步到本地缓存

    # 使用完毕后关闭
    config.close()
```

### 架构说明

```
子进程 configset() ──→ set_queue ──→ 主进程 _loop() ──→ Config.configset()
                                                              │
                                                              ▼
                                                     config_get_queue
                                                              │
                                                              ▼
                                             子进程 _update_config_loop()
                                             更新本地 self.config 缓存
```

- 子进程的 `configset()` 和 `configreset()` 不直接写文件，而是通过队列将请求发送给主进程
- 主进程统一处理所有写操作和文件 I/O，修改后通过广播队列通知所有子进程
- 子进程的 `configget()` 从本地缓存读取，无跨进程开销
- 每个子进程需要独立的管道对（一个 `ConfigInMainProcessPipe` + 一个 `ConfigInChildProcessPipe`）

## API 参考

### Config

核心配置类，管理 JSON 文件的读写。

```python
Config(file_name="config", default_config=None, default_config_callback=None, init_use_callback=True)
```

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `file_name` | `str` | `"config"` | 配置文件名，不含 `.json` 扩展名 |
| `default_config` | `dict` | `None` | 默认配置字典，缺失的 key 自动补齐 |
| `default_config_callback` | `Callable` | `None` | 默认配置回调函数，重置时动态生成默认值。接受当前 config 字典作为参数 |
| `init_use_callback` | `bool` | `True` | 是否在初始化时调用回调来填充 `default_config` |

**方法：**

| 方法 | 说明 |
|------|------|
| `configget(key)` | 获取配置值。`key="all"` 返回全部配置 |
| `configset(key, value)` | 设置配置值，自动保存到文件并通知子进程 |
| `configreset(key)` | 恢复默认值。`key="all"` 恢复全部 |
| `configupdate_config(key, last_value=None)` | 更新默认值，用于版本更新时希望修改软件的默认值。若用户未修改过该 key 则同步为新默认值。需要手动创建一项"{原配置key}_reseted"=False的配置，用于标记是否执行过更新默认值的操作。 |
| `update_config()` | 向所有已注册的子进程广播当前配置 |

### ConfigInMainProcessPipe

主进程管道接口，在主进程中每个子进程对应创建一个实例。

```python
ConfigInMainProcessPipe(config: Config)
```

| 方法 | 说明 |
|------|------|
| `get_queues()` | 返回 `(set_queue, get_queue)` 元组，传递给子进程 |
| `close()` | 关闭管道，停止监听线程 |

### ConfigInChildProcessPipe

子进程配置工具，在子进程中实例化，通过管道与主进程通信。

```python
ConfigInChildProcessPipe(config_set_queue, config_get_queue)
```

| 方法 | 说明 |
|------|------|
| `configget(key)` | 从本地缓存读取配置值 |
| `configset(key, value)` | 修改配置（消息发送给主进程执行） |
| `configreset(key)` | 恢复默认值（消息发送给主进程执行） |
| `close()` | 关闭管道，停止同步线程 |

## 注意事项

1. **多进程环境下请保持 Config 单例** — 多个 Config 实例同时操作同一文件会导致数据不一致
2. **子进程中的 ConfigInChildProcessPipe 不直接操作文件** — 所有写操作将发送请求，由主进程统一处理
3. **使用完毕后请调用 `close()`** — 避免守护线程未正常退出
4. **配置文件名不含 `.json` 后缀** — 传入 `"myconfig"` 对应文件 `myconfig.json`

## 许可证

MIT
