funtable


Namefuntable JSON
Version 1.0.41 PyPI version JSON
download
home_pageNone
SummaryA unified table storage abstraction library with support for multiple backends
upload_time2025-08-23 06:28:23
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords table storage database kv key-value sqlite tinydb sqlmodel orm abstraction
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # FunTable

[![PyPI version](https://badge.fury.io/py/funtable.svg)](https://badge.fury.io/py/funtable)
[![Python](https://img.shields.io/pypi/pyversions/funtable.svg)](https://pypi.org/project/funtable/)
[![License](https://img.shields.io/github/license/farfarfun/funtable.svg)](https://github.com/farfarfun/funtable/blob/main/LICENSE)

一个统一的表存储抽象库,为各种存储后端(包括 SQLite、TinyDB、SQLModel 和云存储快照)提供一致的接口。

## 特性

- **统一接口**: 跨不同存储后端的一致 API
- **多种存储类型**: 支持 KV(键值)和 KKV(键键值)存储模式
- **事务支持**: 内置事务管理,确保数据一致性
- **多种后端**: 支持 SQLite、TinyDB、SQLModel 和云存储
- **快照管理**: 自动备份和版本管理,支持云存储
- **线程安全**: 为并发访问场景而设计

## 安装

### 基础安装

```bash
pip install funtable
```

### 安装可选依赖

```bash
# 支持 SQLModel
pip install funtable[sqlmodel]

# 支持快照/云存储
pip install funtable[snapshot]

# 支持 TinyDB
pip install funtable[kv]

# 安装所有可选依赖
pip install funtable[sqlmodel,snapshot,kv]
```

## 快速开始

### KV(键值)存储

```python
from funtable.kv import SQLiteDB

# 初始化数据库
db = SQLiteDB("my_database.db")

# 创建 KV 表
db.create_kv_table("users")
table = db.get_table("users")

# 存储数据
table.set("user1", {"name": "Alice", "age": 30})
table.set("user2", {"name": "Bob", "age": 25})

# 检索数据
user = table.get("user1")
print(user)  # {"name": "Alice", "age": 30}

# 列出所有键
keys = table.list_keys()
print(keys)  # ["user1", "user2"]

# 获取所有数据
all_data = table.list_all()
print(all_data)
```

### KKV(键键值)存储

```python
from funtable.kv import SQLiteDB

# 初始化数据库
db = SQLiteDB("my_database.db")

# 创建 KKV 表
db.create_kkv_table("user_profiles")
table = db.get_table("user_profiles")

# 使用两级键存储数据
table.set("user1", "profile", {"bio": "Software Engineer"})
table.set("user1", "settings", {"theme": "dark", "notifications": True})
table.set("user2", "profile", {"bio": "Data Scientist"})

# 检索数据
profile = table.get("user1", "profile")
print(profile)  # {"bio": "Software Engineer"}

# 列出主键
pkeys = table.list_pkeys()
print(pkeys)  # ["user1", "user2"]

# 列出某个主键的次键
skeys = table.list_skeys("user1")
print(skeys)  # ["profile", "settings"]
```

### 事务支持

```python
# 使用事务确保数据一致性
table.begin_transaction()
try:
    table.set("user3", {"name": "Charlie", "age": 35})
    table.set("user4", {"name": "David", "age": 40})
    table.commit()
except Exception as e:
    table.rollback()
    print(f"事务失败: {e}")
```

### SQLModel 集成

```python
from funtable.sqlmodel import BaseModel
from sqlmodel import Field
from datetime import datetime

class User(BaseModel, table=True):
    name: str = Field(description="用户名")
    email: str = Field(description="用户邮箱")
    age: int = Field(description="用户年龄")

# BaseModel 提供内置字段:
# - id: 自增主键
# - uid: 唯一标识符
# - gmt_create: 创建时间戳
# - gmt_modified: 修改时间戳

# 与 SQLModel session 一起使用
from sqlmodel import create_engine, Session

engine = create_engine("sqlite:///users.db")
with Session(engine) as session:
    user = User(name="Alice", email="alice@example.com", age=30)
    session.add(user)
    session.commit()
    
    # 查询方法
    all_users = User.all(session)
    user_by_id = User.by_id(1, session)
    user_by_uid = User.by_uid("some-uid", session)
```

### 快照管理

```python
from funtable.snapshot import DriveSnapshot
from fundrive import SomeDriveImplementation

# 初始化驱动器和快照管理器
drive = SomeDriveImplementation()
snapshot = DriveSnapshot(
    table_fid="my-table-id",
    drive=drive,
    num=7  # 保留 7 个版本
)

# 创建快照
snapshot.update("/path/to/data", partition="20231201")

# 下载最新快照
snapshot.download("/path/to/restore")
```

## 存储后端

### SQLite 后端
- **文件**: `funtable.kv.sqlite_table`
- **特性**: ACID 事务、SQL 查询、基于文件的存储
- **适用于**: 生产应用、复杂查询、数据完整性

### TinyDB 后端
- **文件**: `funtable.kv.tinydb_table`
- **特性**: 基于 JSON、轻量级、无外部依赖
- **适用于**: 小型应用、原型开发、简单数据结构

### SQLModel 集成
- **文件**: `funtable.sqlmodel.base`
- **特性**: ORM 功能、类型安全、Pydantic 集成
- **适用于**: 现代 Python 应用、API 开发、数据验证

### 云存储快照
- **文件**: `funtable.snapshot.core`
- **特性**: 自动备份、版本管理、云集成
- **适用于**: 数据备份、灾难恢复、分布式系统

## API 参考

### 核心接口

#### BaseKVTable
- `set(key: str, value: Dict) -> None`: 存储键值对
- `get(key: str) -> Optional[Dict]`: 通过键检索值
- `delete(key: str) -> bool`: 删除键值对
- `list_keys() -> List[str]`: 获取所有键
- `list_all() -> Dict[str, Dict]`: 获取所有键值对
- `begin_transaction()`, `commit()`, `rollback()`: 事务管理

#### BaseKKVTable
- `set(pkey: str, skey: str, value: Dict) -> None`: 使用两级键存储
- `get(pkey: str, skey: str) -> Optional[Dict]`: 通过两级键检索
- `delete(pkey: str, skey: str) -> bool`: 通过两级键删除
- `list_pkeys() -> List[str]`: 获取所有主键
- `list_skeys(pkey: str) -> List[str]`: 获取主键的次键
- `list_all() -> Dict[str, Dict[str, Dict]]`: 获取所有数据
- `batch_set(items: Dict) -> None`: 批量插入操作
- `batch_delete(items: List[tuple]) -> None`: 批量删除操作

#### BaseDB
- `create_kv_table(table_name: str) -> None`: 创建 KV 表
- `create_kkv_table(table_name: str) -> None`: 创建 KKV 表
- `get_table(table_name: str) -> Union[BaseKVTable, BaseKKVTable]`: 获取表实例
- `list_tables() -> Dict[str, str]`: 列出所有表及其类型
- `drop_table(table_name: str) -> None`: 删除表

## 错误处理

所有存储操作都可能引发 `StoreError` 异常:

```python
from funtable.kv.interface import StoreError

try:
    table.set("key", {"data": "value"})
except StoreError as e:
    print(f"存储错误: {e}")
    if e.cause:
        print(f"原因: {e.cause}")
```

## 贡献

1. Fork 仓库
2. 创建功能分支 (`git checkout -b feature/amazing-feature`)
3. 提交更改 (`git commit -m 'Add amazing feature'`)
4. 推送到分支 (`git push origin feature/amazing-feature`)
5. 打开 Pull Request

## 许可证

本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。

## 链接

- **仓库**: https://github.com/farfarfun/funtable
- **PyPI**: https://pypi.org/project/funtable/
- **问题**: https://github.com/farfarfun/funtable/issues
- **发布**: https://github.com/farfarfun/funtable/releases

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "funtable",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "\u725b\u54e5 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>",
    "keywords": "table, storage, database, kv, key-value, sqlite, tinydb, sqlmodel, orm, abstraction",
    "author": null,
    "author_email": "\u725b\u54e5 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>",
    "download_url": null,
    "platform": null,
    "description": "# FunTable\n\n[![PyPI version](https://badge.fury.io/py/funtable.svg)](https://badge.fury.io/py/funtable)\n[![Python](https://img.shields.io/pypi/pyversions/funtable.svg)](https://pypi.org/project/funtable/)\n[![License](https://img.shields.io/github/license/farfarfun/funtable.svg)](https://github.com/farfarfun/funtable/blob/main/LICENSE)\n\n\u4e00\u4e2a\u7edf\u4e00\u7684\u8868\u5b58\u50a8\u62bd\u8c61\u5e93\uff0c\u4e3a\u5404\u79cd\u5b58\u50a8\u540e\u7aef\uff08\u5305\u62ec SQLite\u3001TinyDB\u3001SQLModel \u548c\u4e91\u5b58\u50a8\u5feb\u7167\uff09\u63d0\u4f9b\u4e00\u81f4\u7684\u63a5\u53e3\u3002\n\n## \u7279\u6027\n\n- **\u7edf\u4e00\u63a5\u53e3**: \u8de8\u4e0d\u540c\u5b58\u50a8\u540e\u7aef\u7684\u4e00\u81f4 API\n- **\u591a\u79cd\u5b58\u50a8\u7c7b\u578b**: \u652f\u6301 KV\uff08\u952e\u503c\uff09\u548c KKV\uff08\u952e\u952e\u503c\uff09\u5b58\u50a8\u6a21\u5f0f\n- **\u4e8b\u52a1\u652f\u6301**: \u5185\u7f6e\u4e8b\u52a1\u7ba1\u7406\uff0c\u786e\u4fdd\u6570\u636e\u4e00\u81f4\u6027\n- **\u591a\u79cd\u540e\u7aef**: \u652f\u6301 SQLite\u3001TinyDB\u3001SQLModel \u548c\u4e91\u5b58\u50a8\n- **\u5feb\u7167\u7ba1\u7406**: \u81ea\u52a8\u5907\u4efd\u548c\u7248\u672c\u7ba1\u7406\uff0c\u652f\u6301\u4e91\u5b58\u50a8\n- **\u7ebf\u7a0b\u5b89\u5168**: \u4e3a\u5e76\u53d1\u8bbf\u95ee\u573a\u666f\u800c\u8bbe\u8ba1\n\n## \u5b89\u88c5\n\n### \u57fa\u7840\u5b89\u88c5\n\n```bash\npip install funtable\n```\n\n### \u5b89\u88c5\u53ef\u9009\u4f9d\u8d56\n\n```bash\n# \u652f\u6301 SQLModel\npip install funtable[sqlmodel]\n\n# \u652f\u6301\u5feb\u7167/\u4e91\u5b58\u50a8\npip install funtable[snapshot]\n\n# \u652f\u6301 TinyDB\npip install funtable[kv]\n\n# \u5b89\u88c5\u6240\u6709\u53ef\u9009\u4f9d\u8d56\npip install funtable[sqlmodel,snapshot,kv]\n```\n\n## \u5feb\u901f\u5f00\u59cb\n\n### KV\uff08\u952e\u503c\uff09\u5b58\u50a8\n\n```python\nfrom funtable.kv import SQLiteDB\n\n# \u521d\u59cb\u5316\u6570\u636e\u5e93\ndb = SQLiteDB(\"my_database.db\")\n\n# \u521b\u5efa KV \u8868\ndb.create_kv_table(\"users\")\ntable = db.get_table(\"users\")\n\n# \u5b58\u50a8\u6570\u636e\ntable.set(\"user1\", {\"name\": \"Alice\", \"age\": 30})\ntable.set(\"user2\", {\"name\": \"Bob\", \"age\": 25})\n\n# \u68c0\u7d22\u6570\u636e\nuser = table.get(\"user1\")\nprint(user)  # {\"name\": \"Alice\", \"age\": 30}\n\n# \u5217\u51fa\u6240\u6709\u952e\nkeys = table.list_keys()\nprint(keys)  # [\"user1\", \"user2\"]\n\n# \u83b7\u53d6\u6240\u6709\u6570\u636e\nall_data = table.list_all()\nprint(all_data)\n```\n\n### KKV\uff08\u952e\u952e\u503c\uff09\u5b58\u50a8\n\n```python\nfrom funtable.kv import SQLiteDB\n\n# \u521d\u59cb\u5316\u6570\u636e\u5e93\ndb = SQLiteDB(\"my_database.db\")\n\n# \u521b\u5efa KKV \u8868\ndb.create_kkv_table(\"user_profiles\")\ntable = db.get_table(\"user_profiles\")\n\n# \u4f7f\u7528\u4e24\u7ea7\u952e\u5b58\u50a8\u6570\u636e\ntable.set(\"user1\", \"profile\", {\"bio\": \"Software Engineer\"})\ntable.set(\"user1\", \"settings\", {\"theme\": \"dark\", \"notifications\": True})\ntable.set(\"user2\", \"profile\", {\"bio\": \"Data Scientist\"})\n\n# \u68c0\u7d22\u6570\u636e\nprofile = table.get(\"user1\", \"profile\")\nprint(profile)  # {\"bio\": \"Software Engineer\"}\n\n# \u5217\u51fa\u4e3b\u952e\npkeys = table.list_pkeys()\nprint(pkeys)  # [\"user1\", \"user2\"]\n\n# \u5217\u51fa\u67d0\u4e2a\u4e3b\u952e\u7684\u6b21\u952e\nskeys = table.list_skeys(\"user1\")\nprint(skeys)  # [\"profile\", \"settings\"]\n```\n\n### \u4e8b\u52a1\u652f\u6301\n\n```python\n# \u4f7f\u7528\u4e8b\u52a1\u786e\u4fdd\u6570\u636e\u4e00\u81f4\u6027\ntable.begin_transaction()\ntry:\n    table.set(\"user3\", {\"name\": \"Charlie\", \"age\": 35})\n    table.set(\"user4\", {\"name\": \"David\", \"age\": 40})\n    table.commit()\nexcept Exception as e:\n    table.rollback()\n    print(f\"\u4e8b\u52a1\u5931\u8d25: {e}\")\n```\n\n### SQLModel \u96c6\u6210\n\n```python\nfrom funtable.sqlmodel import BaseModel\nfrom sqlmodel import Field\nfrom datetime import datetime\n\nclass User(BaseModel, table=True):\n    name: str = Field(description=\"\u7528\u6237\u540d\")\n    email: str = Field(description=\"\u7528\u6237\u90ae\u7bb1\")\n    age: int = Field(description=\"\u7528\u6237\u5e74\u9f84\")\n\n# BaseModel \u63d0\u4f9b\u5185\u7f6e\u5b57\u6bb5:\n# - id: \u81ea\u589e\u4e3b\u952e\n# - uid: \u552f\u4e00\u6807\u8bc6\u7b26\n# - gmt_create: \u521b\u5efa\u65f6\u95f4\u6233\n# - gmt_modified: \u4fee\u6539\u65f6\u95f4\u6233\n\n# \u4e0e SQLModel session \u4e00\u8d77\u4f7f\u7528\nfrom sqlmodel import create_engine, Session\n\nengine = create_engine(\"sqlite:///users.db\")\nwith Session(engine) as session:\n    user = User(name=\"Alice\", email=\"alice@example.com\", age=30)\n    session.add(user)\n    session.commit()\n    \n    # \u67e5\u8be2\u65b9\u6cd5\n    all_users = User.all(session)\n    user_by_id = User.by_id(1, session)\n    user_by_uid = User.by_uid(\"some-uid\", session)\n```\n\n### \u5feb\u7167\u7ba1\u7406\n\n```python\nfrom funtable.snapshot import DriveSnapshot\nfrom fundrive import SomeDriveImplementation\n\n# \u521d\u59cb\u5316\u9a71\u52a8\u5668\u548c\u5feb\u7167\u7ba1\u7406\u5668\ndrive = SomeDriveImplementation()\nsnapshot = DriveSnapshot(\n    table_fid=\"my-table-id\",\n    drive=drive,\n    num=7  # \u4fdd\u7559 7 \u4e2a\u7248\u672c\n)\n\n# \u521b\u5efa\u5feb\u7167\nsnapshot.update(\"/path/to/data\", partition=\"20231201\")\n\n# \u4e0b\u8f7d\u6700\u65b0\u5feb\u7167\nsnapshot.download(\"/path/to/restore\")\n```\n\n## \u5b58\u50a8\u540e\u7aef\n\n### SQLite \u540e\u7aef\n- **\u6587\u4ef6**: `funtable.kv.sqlite_table`\n- **\u7279\u6027**: ACID \u4e8b\u52a1\u3001SQL \u67e5\u8be2\u3001\u57fa\u4e8e\u6587\u4ef6\u7684\u5b58\u50a8\n- **\u9002\u7528\u4e8e**: \u751f\u4ea7\u5e94\u7528\u3001\u590d\u6742\u67e5\u8be2\u3001\u6570\u636e\u5b8c\u6574\u6027\n\n### TinyDB \u540e\u7aef\n- **\u6587\u4ef6**: `funtable.kv.tinydb_table`\n- **\u7279\u6027**: \u57fa\u4e8e JSON\u3001\u8f7b\u91cf\u7ea7\u3001\u65e0\u5916\u90e8\u4f9d\u8d56\n- **\u9002\u7528\u4e8e**: \u5c0f\u578b\u5e94\u7528\u3001\u539f\u578b\u5f00\u53d1\u3001\u7b80\u5355\u6570\u636e\u7ed3\u6784\n\n### SQLModel \u96c6\u6210\n- **\u6587\u4ef6**: `funtable.sqlmodel.base`\n- **\u7279\u6027**: ORM \u529f\u80fd\u3001\u7c7b\u578b\u5b89\u5168\u3001Pydantic \u96c6\u6210\n- **\u9002\u7528\u4e8e**: \u73b0\u4ee3 Python \u5e94\u7528\u3001API \u5f00\u53d1\u3001\u6570\u636e\u9a8c\u8bc1\n\n### \u4e91\u5b58\u50a8\u5feb\u7167\n- **\u6587\u4ef6**: `funtable.snapshot.core`\n- **\u7279\u6027**: \u81ea\u52a8\u5907\u4efd\u3001\u7248\u672c\u7ba1\u7406\u3001\u4e91\u96c6\u6210\n- **\u9002\u7528\u4e8e**: \u6570\u636e\u5907\u4efd\u3001\u707e\u96be\u6062\u590d\u3001\u5206\u5e03\u5f0f\u7cfb\u7edf\n\n## API \u53c2\u8003\n\n### \u6838\u5fc3\u63a5\u53e3\n\n#### BaseKVTable\n- `set(key: str, value: Dict) -> None`: \u5b58\u50a8\u952e\u503c\u5bf9\n- `get(key: str) -> Optional[Dict]`: \u901a\u8fc7\u952e\u68c0\u7d22\u503c\n- `delete(key: str) -> bool`: \u5220\u9664\u952e\u503c\u5bf9\n- `list_keys() -> List[str]`: \u83b7\u53d6\u6240\u6709\u952e\n- `list_all() -> Dict[str, Dict]`: \u83b7\u53d6\u6240\u6709\u952e\u503c\u5bf9\n- `begin_transaction()`, `commit()`, `rollback()`: \u4e8b\u52a1\u7ba1\u7406\n\n#### BaseKKVTable\n- `set(pkey: str, skey: str, value: Dict) -> None`: \u4f7f\u7528\u4e24\u7ea7\u952e\u5b58\u50a8\n- `get(pkey: str, skey: str) -> Optional[Dict]`: \u901a\u8fc7\u4e24\u7ea7\u952e\u68c0\u7d22\n- `delete(pkey: str, skey: str) -> bool`: \u901a\u8fc7\u4e24\u7ea7\u952e\u5220\u9664\n- `list_pkeys() -> List[str]`: \u83b7\u53d6\u6240\u6709\u4e3b\u952e\n- `list_skeys(pkey: str) -> List[str]`: \u83b7\u53d6\u4e3b\u952e\u7684\u6b21\u952e\n- `list_all() -> Dict[str, Dict[str, Dict]]`: \u83b7\u53d6\u6240\u6709\u6570\u636e\n- `batch_set(items: Dict) -> None`: \u6279\u91cf\u63d2\u5165\u64cd\u4f5c\n- `batch_delete(items: List[tuple]) -> None`: \u6279\u91cf\u5220\u9664\u64cd\u4f5c\n\n#### BaseDB\n- `create_kv_table(table_name: str) -> None`: \u521b\u5efa KV \u8868\n- `create_kkv_table(table_name: str) -> None`: \u521b\u5efa KKV \u8868\n- `get_table(table_name: str) -> Union[BaseKVTable, BaseKKVTable]`: \u83b7\u53d6\u8868\u5b9e\u4f8b\n- `list_tables() -> Dict[str, str]`: \u5217\u51fa\u6240\u6709\u8868\u53ca\u5176\u7c7b\u578b\n- `drop_table(table_name: str) -> None`: \u5220\u9664\u8868\n\n## \u9519\u8bef\u5904\u7406\n\n\u6240\u6709\u5b58\u50a8\u64cd\u4f5c\u90fd\u53ef\u80fd\u5f15\u53d1 `StoreError` \u5f02\u5e38\uff1a\n\n```python\nfrom funtable.kv.interface import StoreError\n\ntry:\n    table.set(\"key\", {\"data\": \"value\"})\nexcept StoreError as e:\n    print(f\"\u5b58\u50a8\u9519\u8bef: {e}\")\n    if e.cause:\n        print(f\"\u539f\u56e0: {e.cause}\")\n```\n\n## \u8d21\u732e\n\n1. Fork \u4ed3\u5e93\n2. \u521b\u5efa\u529f\u80fd\u5206\u652f (`git checkout -b feature/amazing-feature`)\n3. \u63d0\u4ea4\u66f4\u6539 (`git commit -m 'Add amazing feature'`)\n4. \u63a8\u9001\u5230\u5206\u652f (`git push origin feature/amazing-feature`)\n5. \u6253\u5f00 Pull Request\n\n## \u8bb8\u53ef\u8bc1\n\n\u672c\u9879\u76ee\u91c7\u7528 MIT \u8bb8\u53ef\u8bc1 - \u8be6\u89c1 [LICENSE](LICENSE) \u6587\u4ef6\u3002\n\n## \u94fe\u63a5\n\n- **\u4ed3\u5e93**: https://github.com/farfarfun/funtable\n- **PyPI**: https://pypi.org/project/funtable/\n- **\u95ee\u9898**: https://github.com/farfarfun/funtable/issues\n- **\u53d1\u5e03**: https://github.com/farfarfun/funtable/releases\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A unified table storage abstraction library with support for multiple backends",
    "version": "1.0.41",
    "project_urls": {
        "Organization": "https://github.com/farfarfun",
        "Releases": "https://github.com/farfarfun/funtable/releases",
        "Repository": "https://github.com/farfarfun/funtable"
    },
    "split_keywords": [
        "table",
        " storage",
        " database",
        " kv",
        " key-value",
        " sqlite",
        " tinydb",
        " sqlmodel",
        " orm",
        " abstraction"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "45a66760c69ec39147e7299a236bccf9205ced845164a9f76205ae45a778d118",
                "md5": "67e6902c537e0da81d445347669f4e3e",
                "sha256": "e4f40cae517a746d9f2b68359e7cd032dc4ccf3b2b7832724f3aa7d3ef812be2"
            },
            "downloads": -1,
            "filename": "funtable-1.0.41-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "67e6902c537e0da81d445347669f4e3e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 18537,
            "upload_time": "2025-08-23T06:28:23",
            "upload_time_iso_8601": "2025-08-23T06:28:23.612478Z",
            "url": "https://files.pythonhosted.org/packages/45/a6/6760c69ec39147e7299a236bccf9205ced845164a9f76205ae45a778d118/funtable-1.0.41-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-23 06:28:23",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "farfarfun",
    "github_project": "funtable",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "funtable"
}
        
Elapsed time: 1.50791s