# Py-Tools
> Py-Tools 是一个实用的 Python 工具集和可复用组件库,旨在简化常见任务,提高 Python 项目的开发效率。
>
> 设计细节请移步到掘金查看:https://juejin.cn/column/7131286129713610766
## 安装
- 环境要求:python version >= 3.8
- 历史版本记录:https://pypi.org/project/hui-tools/#history
### 默认安装
```python
pip install hui-tools
```
默认安装如下功能可以使用
- 时间工具类
- http客户端
- 同步异步互转装饰器
- 常用枚举
- pydantic
- loguru的日志器
- jwt工具类
- 等...
### 全部安装
```python
pip install hui-tools[all]
```
### 可选安装
```python
pip install hui-tools[db-orm, db-redis, excel-tools]
```
可选参数参考:
```python
extras_require = {
"db-orm": ["sqlalchemy[asyncio]==2.0.20", "aiomysql==0.2.0"],
"db-redis": ["redis>=4.5.4"],
"cache-proxy": ["redis>=4.5.4", "python-memcached==1.62", "cacheout==0.14.1"],
"minio": ["minio==7.1.17"],
"excel-tools": ["pandas==2.2.2", "openpyxl==3.0.10"],
"test": ["pytest==7.3.1", "pytest-mock==3.14.0", "pytest-asyncio==0.23.8"],
}
```
### 简单使用
> 所有功能都是从 py_tools 包中导入使用
> 详细使用请查看项目的DEMO: https://github.com/HuiDBK/py-tools/tree/master/demo
生成python web项目结构模板
```python
py_tools make_project WebDemo
```
快速配置项目日志
```python
from py_tools.constants import BASE_DIR
from py_tools.logging import logger, setup_logging
def main():
setup_logging(log_dir=BASE_DIR / "logs")
logger.info("use log dir")
logger.error("test error")
if __name__ == '__main__':
main()
```
异步http客户端
```python
import asyncio
from py_tools.connections.http import AsyncHttpClient
async def main():
url = "https://juejin.cn/"
resp = await AsyncHttpClient().get(url).execute()
text_data = await AsyncHttpClient().get(url, params={"test": "hui"}).text()
json_data = await AsyncHttpClient().post(url, data={"test": "hui"}).json()
byte_data = await AsyncHttpClient().get(url).bytes()
async with AsyncHttpClient() as client:
upload_file_ret = await client.upload_file(url, file="test.txt").json()
async for chunk in AsyncHttpClient().get(url).stream(chunk_size=512):
# 流式调用
print(chunk)
await AsyncHttpClient.close()
if __name__ == "__main__":
asyncio.run(main())
```
mysql数据库操作demo
```python
import asyncio
import uuid
from typing import List
from connections.sqlalchemy_demo.manager import UserFileManager
from connections.sqlalchemy_demo.table import UserFileTable
from sqlalchemy import func
from py_tools.connections.db.mysql import BaseOrmTable, DBManager, SQLAlchemyManager
db_client = SQLAlchemyManager(
host="127.0.0.1",
port=3306,
user="root",
password="123456",
db_name="hui-demo",
)
async def create_and_transaction_demo():
async with UserFileManager.transaction() as session:
await UserFileManager(session).bulk_add(table_objs=[{"filename": "aaa", "oss_key": uuid.uuid4().hex}])
user_file_obj = UserFileTable(filename="eee", oss_key=uuid.uuid4().hex)
file_id = await UserFileManager(session).add(table_obj=user_file_obj)
print("file_id", file_id)
ret: UserFileTable = await UserFileManager(session).query_by_id(2)
print("query_by_id", ret)
# a = 1 / 0
ret = await UserFileManager(session).query_one(
cols=[UserFileTable.filename, UserFileTable.oss_key], conds=[UserFileTable.filename == "ccc"],
)
print("ret", ret)
async def query_demo():
ret = await UserFileManager().query_one(conds=[UserFileTable.filename == "ccc"])
print("ret", ret)
file_count = await UserFileManager().query_one(cols=[func.count()], flat=True)
print("str col one ret", file_count)
filename = await UserFileManager().query_one(cols=[UserFileTable.filename], conds=[UserFileTable.id == 2], flat=True)
print("filename", filename)
ret = await UserFileManager().query_all(cols=[UserFileTable.filename, UserFileTable.oss_key])
print("ret", ret)
ret = await UserFileManager().query_all(cols=["filename", "oss_key"])
print("str col ret", ret)
ret: List[UserFileTable] = await UserFileManager().query_all()
print("ret", ret)
ret = await UserFileManager().query_all(cols=[UserFileTable.id], flat=True)
print("ret", ret)
async def list_page_demo():
"""分页查询demo"""
total_count, data_list = await UserFileManager().list_page(
cols=["filename", "oss_key", "file_size"], curr_page=2, page_size=10
)
print("total_count", total_count, f"data_list[{len(data_list)}]", data_list)
async def run_raw_sql_demo():
"""运行原生sql demo"""
count_sql = "select count(*) as total_count from user_file"
count_ret = await UserFileManager().run_sql(count_sql, query_one=True)
print("count_ret", count_ret)
data_sql = "select * from user_file where id > :id_val and file_size >= :file_size_val"
params = {"id_val": 20, "file_size_val": 0}
data_ret = await UserFileManager().run_sql(data_sql, params=params)
print("dict data_ret", data_ret)
# 连表查询
data_sql = """
select
user.id as user_id,
username,
user_file.id as file_id,
filename,
oss_key
from
user_file
join user on user.id = user_file.creator
where
user_file.creator = :user_id
"""
data_ret = await UserFileManager().run_sql(data_sql, params={"user_id": 1})
print("join sql data_ret", data_ret)
async def curd_demo():
await create_and_transaction_demo()
await query_demo()
await list_page_demo()
await run_raw_sql_demo()
async def create_tables():
# 根据映射创建库表
async with DBManager.connection() as conn:
await conn.run_sync(BaseOrmTable.metadata.create_all)
async def main():
db_client.init_mysql_engine()
DBManager.init_db_client(db_client)
await create_tables()
await curd_demo()
if __name__ == "__main__":
asyncio.run(main())
```
## Todo List
### 连接客户端
1. [x] http 同步异步客户端
2. [x] MySQL 客户端 - SQLAlchemy-ORM 封装
3. [x] Redis 客户端
4. [x] Minio 客户端
5. 消息队列客户端,rabbitmq、kafka
6. websocket 客户端
### 工具类
- [x] 同异步函数转化工具类
- [x] excel 工具类
- [x] 文件 工具类
- [x] 实用函数工具模块
- [x] 数据掩码工具类
- [x] 常用正则工具类
- [x] 时间工具类
- [x] 树结构转换工具类
- [x] pydantic model 、dataclass 与 SQLALChemy table 序列化与反序列工具类
- 认证相关工具类
- [x] JWT 工具类
- 图片操作工具类,例如校验图片分辨率
- 邮件服务工具类
- 配置解析工具类
- 编码工具类,统一 base64、md5等编码入口
- 加密工具类
### 装饰器
1. [x] 超时装饰器
2. [x] 重试装饰器
3. [x] 缓存装饰器
4. [x] 异步执行装饰器
### 枚举
1. [x] 通用枚举类封装
2. [x] 错误码枚举封装
3. [x] 常用枚举
### 异常
1. [x] 业务异常类封装
### 日志
1. [x] logger 日志器(loguru)
2. [x] 快速配置项目日志函数
## 工程目录结构
```
py-tools/
├── py_tools/
│ ├── chatbot/
│ ├── connections/
│ ├── constants/
│ ├── data_schemas/
│ ├── decorators/
│ ├── enums/
│ ├── exceptions/
│ ├── meta_cls/
│ └── utils/
├── docs/
├── demo/
├── tests/
├── .gitignore
├── LICENSE
├── README.md
└── requirements.txt
```
### 项目模块
- **chatbot**: 用于构建和管理聊天机器人互动的工具集。
- **connections**: 用于连接各种服务和 API 的连接管理工具。
- **constants**: Python 项目中常用的常量。
- **data_schemas**: 用于处理结构化数据的数据模型及相关工具。
- **decorators**: 一系列有用的装饰器,用以增强函数和方法。
- **enums**: 定义常用枚举类型,方便在项目中使用。
- **exceptions**: 自定义异常类,用于项目中的错误处理。
- **meta_cls**: 元类和元编程相关的工具和技术。
- **utils**: 包含各种实用函数和工具,用于简化日常编程任务。
### 项目文档
在 `docs` 目录下存放一些项目相关文档。
### 示例
在 `demo` 目录下,您可以找到一些使用 Py-Tools 的示例代码,这些代码展示了如何使用这些工具集实现实际项目中的任务。
demo:https://github.com/HuiDBK/py-tools/tree/master/demo
### 测试
在 `tests` 目录下,包含了针对 Py-Tools 的各个组件的单元测试。通过运行这些测试,您可以确保工具集在您的环境中正常工作。
## 一起贡献
> 欢迎您对本项目进行贡献。请在提交 Pull Request 之前阅读项目的贡献指南,并确保您的代码符合项目的代码风格。
1. Fork后克隆本项目到本地:
```bash
git clone https://github.com/<github_name>/py-tools.git
```
2. 安装依赖:
```python
pip install -r requirements.txt
```
3. 配置python代码风格检查到 git hook 中
安装 pre-commit
```python
pip install pre-commit
```
再项目目录下执行
```python
pre-commit install
```
安装成功后 git commit 后会提前进行代码检查
4. 提PR
Raw data
{
"_id": null,
"home_page": "https://github.com/HuiDBK/py-tools",
"name": "hui-tools",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": null,
"author": "hui",
"author_email": "huidbk@163.com",
"download_url": "https://files.pythonhosted.org/packages/1c/4f/b415c9f0f6ce905d4b888170e458f013238e0c726a0b0876f243806a8aa0/hui-tools-0.5.8.tar.gz",
"platform": null,
"description": "# Py-Tools\n\n> Py-Tools \u662f\u4e00\u4e2a\u5b9e\u7528\u7684 Python \u5de5\u5177\u96c6\u548c\u53ef\u590d\u7528\u7ec4\u4ef6\u5e93\uff0c\u65e8\u5728\u7b80\u5316\u5e38\u89c1\u4efb\u52a1\uff0c\u63d0\u9ad8 Python \u9879\u76ee\u7684\u5f00\u53d1\u6548\u7387\u3002\n> \n> \u8bbe\u8ba1\u7ec6\u8282\u8bf7\u79fb\u6b65\u5230\u6398\u91d1\u67e5\u770b\uff1ahttps://juejin.cn/column/7131286129713610766\n\n## \u5b89\u88c5\n- \u73af\u5883\u8981\u6c42\uff1apython version >= 3.8\n- \u5386\u53f2\u7248\u672c\u8bb0\u5f55\uff1ahttps://pypi.org/project/hui-tools/#history\n\n\n### \u9ed8\u8ba4\u5b89\u88c5\n```python\npip install hui-tools\n```\n\u9ed8\u8ba4\u5b89\u88c5\u5982\u4e0b\u529f\u80fd\u53ef\u4ee5\u4f7f\u7528\n- \u65f6\u95f4\u5de5\u5177\u7c7b\n- http\u5ba2\u6237\u7aef\n- \u540c\u6b65\u5f02\u6b65\u4e92\u8f6c\u88c5\u9970\u5668\n- \u5e38\u7528\u679a\u4e3e\n- pydantic\n- loguru\u7684\u65e5\u5fd7\u5668\n- jwt\u5de5\u5177\u7c7b\n- \u7b49...\n\n### \u5168\u90e8\u5b89\u88c5\n```python\npip install hui-tools[all]\n```\n\n### \u53ef\u9009\u5b89\u88c5\n```python\npip install hui-tools[db-orm, db-redis, excel-tools]\n```\n\n\u53ef\u9009\u53c2\u6570\u53c2\u8003\uff1a\n```python\nextras_require = {\n \"db-orm\": [\"sqlalchemy[asyncio]==2.0.20\", \"aiomysql==0.2.0\"],\n \"db-redis\": [\"redis>=4.5.4\"],\n \"cache-proxy\": [\"redis>=4.5.4\", \"python-memcached==1.62\", \"cacheout==0.14.1\"],\n \"minio\": [\"minio==7.1.17\"],\n \"excel-tools\": [\"pandas==2.2.2\", \"openpyxl==3.0.10\"],\n \"test\": [\"pytest==7.3.1\", \"pytest-mock==3.14.0\", \"pytest-asyncio==0.23.8\"],\n}\n```\n\n### \u7b80\u5355\u4f7f\u7528\n> \u6240\u6709\u529f\u80fd\u90fd\u662f\u4ece py_tools \u5305\u4e2d\u5bfc\u5165\u4f7f\u7528\n> \u8be6\u7ec6\u4f7f\u7528\u8bf7\u67e5\u770b\u9879\u76ee\u7684DEMO\uff1a https://github.com/HuiDBK/py-tools/tree/master/demo\n\n\u751f\u6210python web\u9879\u76ee\u7ed3\u6784\u6a21\u677f\n```python\npy_tools make_project WebDemo\n```\n\n\u5feb\u901f\u914d\u7f6e\u9879\u76ee\u65e5\u5fd7\n```python\nfrom py_tools.constants import BASE_DIR\nfrom py_tools.logging import logger, setup_logging\n\n\ndef main():\n setup_logging(log_dir=BASE_DIR / \"logs\")\n logger.info(\"use log dir\")\n logger.error(\"test error\")\n\n\nif __name__ == '__main__':\n main()\n```\n\n\u5f02\u6b65http\u5ba2\u6237\u7aef\n```python\nimport asyncio\nfrom py_tools.connections.http import AsyncHttpClient\n\n\nasync def main():\n url = \"https://juejin.cn/\"\n resp = await AsyncHttpClient().get(url).execute()\n text_data = await AsyncHttpClient().get(url, params={\"test\": \"hui\"}).text()\n json_data = await AsyncHttpClient().post(url, data={\"test\": \"hui\"}).json()\n byte_data = await AsyncHttpClient().get(url).bytes()\n \n async with AsyncHttpClient() as client:\n upload_file_ret = await client.upload_file(url, file=\"test.txt\").json()\n \n async for chunk in AsyncHttpClient().get(url).stream(chunk_size=512):\n # \u6d41\u5f0f\u8c03\u7528\n print(chunk)\n \n await AsyncHttpClient.close()\n\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n```\n\nmysql\u6570\u636e\u5e93\u64cd\u4f5cdemo\n```python\nimport asyncio\nimport uuid\nfrom typing import List\n\nfrom connections.sqlalchemy_demo.manager import UserFileManager\nfrom connections.sqlalchemy_demo.table import UserFileTable\nfrom sqlalchemy import func\n\nfrom py_tools.connections.db.mysql import BaseOrmTable, DBManager, SQLAlchemyManager\n\ndb_client = SQLAlchemyManager(\n host=\"127.0.0.1\",\n port=3306,\n user=\"root\",\n password=\"123456\",\n db_name=\"hui-demo\",\n)\n\n\nasync def create_and_transaction_demo():\n async with UserFileManager.transaction() as session:\n await UserFileManager(session).bulk_add(table_objs=[{\"filename\": \"aaa\", \"oss_key\": uuid.uuid4().hex}])\n user_file_obj = UserFileTable(filename=\"eee\", oss_key=uuid.uuid4().hex)\n file_id = await UserFileManager(session).add(table_obj=user_file_obj)\n print(\"file_id\", file_id)\n\n ret: UserFileTable = await UserFileManager(session).query_by_id(2)\n print(\"query_by_id\", ret)\n\n # a = 1 / 0\n\n ret = await UserFileManager(session).query_one(\n cols=[UserFileTable.filename, UserFileTable.oss_key], conds=[UserFileTable.filename == \"ccc\"],\n )\n print(\"ret\", ret)\n\n\nasync def query_demo():\n ret = await UserFileManager().query_one(conds=[UserFileTable.filename == \"ccc\"])\n print(\"ret\", ret)\n\n file_count = await UserFileManager().query_one(cols=[func.count()], flat=True)\n print(\"str col one ret\", file_count)\n\n filename = await UserFileManager().query_one(cols=[UserFileTable.filename], conds=[UserFileTable.id == 2], flat=True)\n print(\"filename\", filename)\n\n ret = await UserFileManager().query_all(cols=[UserFileTable.filename, UserFileTable.oss_key])\n print(\"ret\", ret)\n\n ret = await UserFileManager().query_all(cols=[\"filename\", \"oss_key\"])\n print(\"str col ret\", ret)\n\n ret: List[UserFileTable] = await UserFileManager().query_all()\n print(\"ret\", ret)\n\n ret = await UserFileManager().query_all(cols=[UserFileTable.id], flat=True)\n print(\"ret\", ret)\n\n\nasync def list_page_demo():\n \"\"\"\u5206\u9875\u67e5\u8be2demo\"\"\"\n total_count, data_list = await UserFileManager().list_page(\n cols=[\"filename\", \"oss_key\", \"file_size\"], curr_page=2, page_size=10\n )\n print(\"total_count\", total_count, f\"data_list[{len(data_list)}]\", data_list)\n\n\nasync def run_raw_sql_demo():\n \"\"\"\u8fd0\u884c\u539f\u751fsql demo\"\"\"\n count_sql = \"select count(*) as total_count from user_file\"\n count_ret = await UserFileManager().run_sql(count_sql, query_one=True)\n print(\"count_ret\", count_ret)\n\n data_sql = \"select * from user_file where id > :id_val and file_size >= :file_size_val\"\n params = {\"id_val\": 20, \"file_size_val\": 0}\n data_ret = await UserFileManager().run_sql(data_sql, params=params)\n print(\"dict data_ret\", data_ret)\n\n # \u8fde\u8868\u67e5\u8be2\n data_sql = \"\"\"\n select\n user.id as user_id,\n username,\n user_file.id as file_id,\n filename,\n oss_key\n from \n user_file\n join user on user.id = user_file.creator\n where \n user_file.creator = :user_id\n \"\"\"\n data_ret = await UserFileManager().run_sql(data_sql, params={\"user_id\": 1})\n print(\"join sql data_ret\", data_ret)\n\n\nasync def curd_demo():\n await create_and_transaction_demo()\n await query_demo()\n await list_page_demo()\n await run_raw_sql_demo()\n\n\nasync def create_tables():\n # \u6839\u636e\u6620\u5c04\u521b\u5efa\u5e93\u8868\n async with DBManager.connection() as conn:\n await conn.run_sync(BaseOrmTable.metadata.create_all)\n\n\nasync def main():\n db_client.init_mysql_engine()\n DBManager.init_db_client(db_client)\n await create_tables()\n await curd_demo()\n\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n\n```\n\n## Todo List\n\n### \u8fde\u63a5\u5ba2\u6237\u7aef\n1. [x] http \u540c\u6b65\u5f02\u6b65\u5ba2\u6237\u7aef\n2. [x] MySQL \u5ba2\u6237\u7aef - SQLAlchemy-ORM \u5c01\u88c5\n3. [x] Redis \u5ba2\u6237\u7aef\n4. [x] Minio \u5ba2\u6237\u7aef \n5. \u6d88\u606f\u961f\u5217\u5ba2\u6237\u7aef\uff0crabbitmq\u3001kafka \n6. websocket \u5ba2\u6237\u7aef\n\n### \u5de5\u5177\u7c7b\n- [x] \u540c\u5f02\u6b65\u51fd\u6570\u8f6c\u5316\u5de5\u5177\u7c7b\n- [x] excel \u5de5\u5177\u7c7b\n- [x] \u6587\u4ef6 \u5de5\u5177\u7c7b\n- [x] \u5b9e\u7528\u51fd\u6570\u5de5\u5177\u6a21\u5757\n- [x] \u6570\u636e\u63a9\u7801\u5de5\u5177\u7c7b\n- [x] \u5e38\u7528\u6b63\u5219\u5de5\u5177\u7c7b\n- [x] \u65f6\u95f4\u5de5\u5177\u7c7b\n- [x] \u6811\u7ed3\u6784\u8f6c\u6362\u5de5\u5177\u7c7b\n- [x] pydantic model \u3001dataclass \u4e0e SQLALChemy table \u5e8f\u5217\u5316\u4e0e\u53cd\u5e8f\u5217\u5de5\u5177\u7c7b\n- \u8ba4\u8bc1\u76f8\u5173\u5de5\u5177\u7c7b\n - [x] JWT \u5de5\u5177\u7c7b\n- \u56fe\u7247\u64cd\u4f5c\u5de5\u5177\u7c7b\uff0c\u4f8b\u5982\u6821\u9a8c\u56fe\u7247\u5206\u8fa8\u7387\n- \u90ae\u4ef6\u670d\u52a1\u5de5\u5177\u7c7b\n- \u914d\u7f6e\u89e3\u6790\u5de5\u5177\u7c7b\n- \u7f16\u7801\u5de5\u5177\u7c7b\uff0c\u7edf\u4e00 base64\u3001md5\u7b49\u7f16\u7801\u5165\u53e3\n- \u52a0\u5bc6\u5de5\u5177\u7c7b\n\n### \u88c5\u9970\u5668\n1. [x] \u8d85\u65f6\u88c5\u9970\u5668\n2. [x] \u91cd\u8bd5\u88c5\u9970\u5668\n3. [x] \u7f13\u5b58\u88c5\u9970\u5668\n4. [x] \u5f02\u6b65\u6267\u884c\u88c5\u9970\u5668\n\n### \u679a\u4e3e\n1. [x] \u901a\u7528\u679a\u4e3e\u7c7b\u5c01\u88c5\n2. [x] \u9519\u8bef\u7801\u679a\u4e3e\u5c01\u88c5\n3. [x] \u5e38\u7528\u679a\u4e3e\n\n### \u5f02\u5e38\n1. [x] \u4e1a\u52a1\u5f02\u5e38\u7c7b\u5c01\u88c5\n\n### \u65e5\u5fd7\n1. [x] logger \u65e5\u5fd7\u5668\uff08loguru\uff09\n2. [x] \u5feb\u901f\u914d\u7f6e\u9879\u76ee\u65e5\u5fd7\u51fd\u6570\n\n## \u5de5\u7a0b\u76ee\u5f55\u7ed3\u6784\n\n```\npy-tools/\n \u251c\u2500\u2500 py_tools/\n \u2502 \u251c\u2500\u2500 chatbot/\n \u2502 \u251c\u2500\u2500 connections/\n \u2502 \u251c\u2500\u2500 constants/\n \u2502 \u251c\u2500\u2500 data_schemas/\n \u2502 \u251c\u2500\u2500 decorators/\n \u2502 \u251c\u2500\u2500 enums/\n \u2502 \u251c\u2500\u2500 exceptions/\n \u2502 \u251c\u2500\u2500 meta_cls/\n \u2502 \u2514\u2500\u2500 utils/\n \u251c\u2500\u2500 docs/\n \u251c\u2500\u2500 demo/\n \u251c\u2500\u2500 tests/\n \u251c\u2500\u2500 .gitignore\n \u251c\u2500\u2500 LICENSE\n \u251c\u2500\u2500 README.md\n \u2514\u2500\u2500 requirements.txt\n```\n\n\n\n### \u9879\u76ee\u6a21\u5757\n\n- **chatbot**: \u7528\u4e8e\u6784\u5efa\u548c\u7ba1\u7406\u804a\u5929\u673a\u5668\u4eba\u4e92\u52a8\u7684\u5de5\u5177\u96c6\u3002\n- **connections**: \u7528\u4e8e\u8fde\u63a5\u5404\u79cd\u670d\u52a1\u548c API \u7684\u8fde\u63a5\u7ba1\u7406\u5de5\u5177\u3002\n- **constants**: Python \u9879\u76ee\u4e2d\u5e38\u7528\u7684\u5e38\u91cf\u3002\n- **data_schemas**: \u7528\u4e8e\u5904\u7406\u7ed3\u6784\u5316\u6570\u636e\u7684\u6570\u636e\u6a21\u578b\u53ca\u76f8\u5173\u5de5\u5177\u3002\n- **decorators**: \u4e00\u7cfb\u5217\u6709\u7528\u7684\u88c5\u9970\u5668\uff0c\u7528\u4ee5\u589e\u5f3a\u51fd\u6570\u548c\u65b9\u6cd5\u3002\n- **enums**: \u5b9a\u4e49\u5e38\u7528\u679a\u4e3e\u7c7b\u578b\uff0c\u65b9\u4fbf\u5728\u9879\u76ee\u4e2d\u4f7f\u7528\u3002\n- **exceptions**: \u81ea\u5b9a\u4e49\u5f02\u5e38\u7c7b\uff0c\u7528\u4e8e\u9879\u76ee\u4e2d\u7684\u9519\u8bef\u5904\u7406\u3002\n- **meta_cls**: \u5143\u7c7b\u548c\u5143\u7f16\u7a0b\u76f8\u5173\u7684\u5de5\u5177\u548c\u6280\u672f\u3002\n- **utils**: \u5305\u542b\u5404\u79cd\u5b9e\u7528\u51fd\u6570\u548c\u5de5\u5177\uff0c\u7528\u4e8e\u7b80\u5316\u65e5\u5e38\u7f16\u7a0b\u4efb\u52a1\u3002\n\n\n\n### \u9879\u76ee\u6587\u6863\n\n\u5728 `docs` \u76ee\u5f55\u4e0b\u5b58\u653e\u4e00\u4e9b\u9879\u76ee\u76f8\u5173\u6587\u6863\u3002\n\n\n\n### \u793a\u4f8b\n\n\u5728 `demo` \u76ee\u5f55\u4e0b\uff0c\u60a8\u53ef\u4ee5\u627e\u5230\u4e00\u4e9b\u4f7f\u7528 Py-Tools \u7684\u793a\u4f8b\u4ee3\u7801\uff0c\u8fd9\u4e9b\u4ee3\u7801\u5c55\u793a\u4e86\u5982\u4f55\u4f7f\u7528\u8fd9\u4e9b\u5de5\u5177\u96c6\u5b9e\u73b0\u5b9e\u9645\u9879\u76ee\u4e2d\u7684\u4efb\u52a1\u3002\n\ndemo\uff1ahttps://github.com/HuiDBK/py-tools/tree/master/demo\n\n### \u6d4b\u8bd5\n\n\u5728 `tests` \u76ee\u5f55\u4e0b\uff0c\u5305\u542b\u4e86\u9488\u5bf9 Py-Tools \u7684\u5404\u4e2a\u7ec4\u4ef6\u7684\u5355\u5143\u6d4b\u8bd5\u3002\u901a\u8fc7\u8fd0\u884c\u8fd9\u4e9b\u6d4b\u8bd5\uff0c\u60a8\u53ef\u4ee5\u786e\u4fdd\u5de5\u5177\u96c6\u5728\u60a8\u7684\u73af\u5883\u4e2d\u6b63\u5e38\u5de5\u4f5c\u3002\n\n\n\n## \u4e00\u8d77\u8d21\u732e\n> \u6b22\u8fce\u60a8\u5bf9\u672c\u9879\u76ee\u8fdb\u884c\u8d21\u732e\u3002\u8bf7\u5728\u63d0\u4ea4 Pull Request \u4e4b\u524d\u9605\u8bfb\u9879\u76ee\u7684\u8d21\u732e\u6307\u5357\uff0c\u5e76\u786e\u4fdd\u60a8\u7684\u4ee3\u7801\u7b26\u5408\u9879\u76ee\u7684\u4ee3\u7801\u98ce\u683c\u3002\n\n1. Fork\u540e\u514b\u9686\u672c\u9879\u76ee\u5230\u672c\u5730\uff1a\n```bash\ngit clone https://github.com/<github_name>/py-tools.git\n```\n\n2. \u5b89\u88c5\u4f9d\u8d56:\n```python\npip install -r requirements.txt\n```\n\n3. \u914d\u7f6epython\u4ee3\u7801\u98ce\u683c\u68c0\u67e5\u5230 git hook \u4e2d\n\n\u5b89\u88c5 pre-commit\n```python\npip install pre-commit\n```\n\n\u518d\u9879\u76ee\u76ee\u5f55\u4e0b\u6267\u884c\n```python\npre-commit install\n```\n\u5b89\u88c5\u6210\u529f\u540e git commit \u540e\u4f1a\u63d0\u524d\u8fdb\u884c\u4ee3\u7801\u68c0\u67e5\n\n4. \u63d0PR\n",
"bugtrack_url": null,
"license": "Apache",
"summary": null,
"version": "0.5.8",
"project_urls": {
"Homepage": "https://github.com/HuiDBK/py-tools"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b75ce77e611ebfdd682f3c87108f23c256b37514190aad0b2b4906509543a64f",
"md5": "80967dc71123aafe1b13156be4cb201e",
"sha256": "eb6c33916ffdfcc9e290949c8972d3b1376be477473ef89b0e989286d71d6438"
},
"downloads": -1,
"filename": "hui_tools-0.5.8-py3-none-any.whl",
"has_sig": false,
"md5_digest": "80967dc71123aafe1b13156be4cb201e",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 74244,
"upload_time": "2024-12-07T15:28:47",
"upload_time_iso_8601": "2024-12-07T15:28:47.767341Z",
"url": "https://files.pythonhosted.org/packages/b7/5c/e77e611ebfdd682f3c87108f23c256b37514190aad0b2b4906509543a64f/hui_tools-0.5.8-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "1c4fb415c9f0f6ce905d4b888170e458f013238e0c726a0b0876f243806a8aa0",
"md5": "276de1ebf81329d4ea3e94ca537ba63e",
"sha256": "ba4cbf8524a59f71690f14b3d287085c8dea7e09178870a2c35a0b84f7c429f6"
},
"downloads": -1,
"filename": "hui-tools-0.5.8.tar.gz",
"has_sig": false,
"md5_digest": "276de1ebf81329d4ea3e94ca537ba63e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 52592,
"upload_time": "2024-12-07T15:28:52",
"upload_time_iso_8601": "2024-12-07T15:28:52.368194Z",
"url": "https://files.pythonhosted.org/packages/1c/4f/b415c9f0f6ce905d4b888170e458f013238e0c726a0b0876f243806a8aa0/hui-tools-0.5.8.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-07 15:28:52",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "HuiDBK",
"github_project": "py-tools",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "openpyxl",
"specs": [
[
"==",
"3.0.10"
]
]
},
{
"name": "pandas",
"specs": [
[
"==",
"2.0.3"
]
]
},
{
"name": "requests",
"specs": [
[
"==",
"2.31.0"
]
]
},
{
"name": "aiohttp",
"specs": [
[
"==",
"3.9.5"
]
]
},
{
"name": "python-dateutil",
"specs": [
[
"==",
"2.8.2"
]
]
},
{
"name": "loguru",
"specs": [
[
"==",
"0.7.2"
]
]
},
{
"name": "cacheout",
"specs": [
[
"==",
"0.14.1"
]
]
},
{
"name": "redis",
"specs": [
[
"==",
"5.0.1"
]
]
},
{
"name": "python-memcached",
"specs": [
[
"==",
"1.62"
]
]
},
{
"name": "pytest",
"specs": [
[
"==",
"7.3.1"
]
]
},
{
"name": "pydantic",
"specs": [
[
"==",
"2.1.1"
]
]
},
{
"name": "sqlalchemy",
"specs": [
[
"==",
"2.0.20"
]
]
},
{
"name": "aiomysql",
"specs": [
[
"==",
"0.2.0"
]
]
},
{
"name": "minio",
"specs": [
[
"==",
"7.1.17"
]
]
},
{
"name": "asgiref",
"specs": [
[
"==",
"3.8.1"
]
]
},
{
"name": "nest_asyncio",
"specs": [
[
"==",
"1.6.0"
]
]
},
{
"name": "tqdm",
"specs": [
[
"==",
"4.66.4"
]
]
},
{
"name": "aiofiles",
"specs": [
[
"==",
"24.1.0"
]
]
},
{
"name": "python-jose",
"specs": [
[
"==",
"3.3.0"
]
]
}
],
"lcname": "hui-tools"
}