52 Commits
v0.5.1 ... dev

Author SHA1 Message Date
f587b62a26 Merge pull request '1.0.0-dev' (#4) from 1.0.0-dev into dev
Reviewed-on: #4
2025-10-18 09:04:46 +08:00
ca20d51edb refactor: 更新文档示例,简化日志使用方式并升级版本至1.0.0 2025-10-18 09:04:08 +08:00
5540a9169a feat: 优化日志系统,添加日志器缓存机制 2025-10-18 08:50:43 +08:00
c5c625f50e refactor: 重构日志系统,统一日志接口并添加map工具 2025-10-17 14:55:41 +08:00
9fce78a59e refactor: 优化日志系统,支持多日志器 2025-09-15 13:35:16 +08:00
8ac539eb79 refactor: 隐藏原始函数 2025-09-15 10:16:09 +08:00
84a57ff0aa Merge pull request 'fix: 优化代码格式和类型安全性' (#1) from github_main into dev
Reviewed-on: #1
2025-08-20 16:09:46 +08:00
ef62dc2075 fix: 优化代码格式和类型安全性 2025-08-20 16:06:47 +08:00
359c9f247a refactor: 添加构建、测试和清理脚本,优化CMakeLists.txt 2025-07-08 17:16:02 +08:00
aecfbbb74a feat:添加python进行conan编写增强 2025-07-08 16:41:48 +08:00
b273fd86e8 fix bug 2025-05-15 10:36:29 +08:00
8fef266119 Merge branch 'dev' 2025-05-15 10:30:53 +08:00
e04a960777 fix 2024-11-28 18:30:27 +08:00
b2eec437cd 添加安装细节 2024-11-25 22:19:04 +08:00
3f5153b110 更新一些自述文件 2024-11-24 20:23:37 +08:00
09dd534675 Merge branch 'dev' of github.com:WangZhongDian/logging into dev 2024-11-23 13:00:04 +08:00
633e91f80b pr 2024-11-23 12:59:56 +08:00
youmetme
fde26f4f42 Merge branch 'main' into dev 2024-11-23 09:02:09 +08:00
214665ec91 更新自述文件 2024-11-23 08:54:10 +08:00
942970336c Merge branch 'main' into dev 2024-11-23 08:44:26 +08:00
1c09c41ea3 #将拦截器改名为过滤器,更加接近职能 2024-11-23 08:08:21 +08:00
9b777e4862 fix 2024-11-21 16:00:49 +08:00
youmetme
0b749c8d1d Merge branch 'main' into dev 2024-11-21 15:57:41 +08:00
5c24b070bb #fix 拦截器对level参数无效 2024-11-21 15:39:50 +08:00
915c18640a 更新版本号 2024-11-21 15:15:46 +08:00
youmetme
feca0ef8e3 Merge branch 'main' into dev 2024-11-21 15:12:14 +08:00
b9abf6c7d3 Multiple substring interceptors 2024-11-21 15:04:21 +08:00
6ac7afd4c2 修改api函数名,加入Default关键字,减除歧义 2024-11-21 12:48:11 +08:00
7afc3d3b41 add english brief 2024-11-21 12:12:46 +08:00
youmetme
a6392e27ce Merge branch 'main' into dev 2024-11-20 11:56:25 +08:00
8a57c43180 fix:使用char偏移单位 2024-11-20 11:50:32 +08:00
e57f0fa02c fix:内存泄露 2024-11-20 11:18:13 +08:00
de03985fef feature:根据文件大小分割日志 2024-11-19 21:31:00 +08:00
youmetme
81cebc7a18 Merge branch 'main' into dev 2024-11-03 11:46:06 +08:00
41004e5735 ersion 0.2.4 2024-11-03 11:43:57 +08:00
de141cbafc test on windows and test chinese char 2024-11-02 22:38:45 +08:00
668f88f8af fix msvc 不支持中文注释,删除中文注释 2024-11-02 20:20:30 +08:00
34f818196e fix 内存分配错误 2024-11-02 17:31:24 +08:00
78341d522c fix 2024-11-02 14:46:25 +08:00
2ecdefa239 fix action on windows 2024-11-02 14:17:53 +08:00
be445cdfe7 fix test on windows action 2024-11-02 13:37:03 +08:00
24f7124dfb Merge branch 'dev' of github.com:WangZhongDian/logging into dev 2024-11-02 13:26:20 +08:00
8eb324ad23 add test on windows action 2024-11-02 13:25:55 +08:00
youmetme
406dbc5a79 Merge branch 'main' into dev 2024-11-02 12:56:40 +08:00
0fa6097bbf Merge branch 'dev' of github.com:WangZhongDian/logging into dev 2024-11-02 12:54:18 +08:00
f6e2c31c85 修复错别字 2024-11-02 12:51:03 +08:00
youmetme
c7c6af7dcf Merge branch 'main' into dev 2024-11-02 12:37:14 +08:00
0d78195e4e test action 2024-11-02 12:34:15 +08:00
01273809da fix:conanfile 2024-11-01 17:30:39 +08:00
4213c38730 加入test脚本 2024-10-10 22:12:50 +08:00
9963f68175 更新版本号 2024-09-21 15:08:39 +08:00
85ae9d3deb #feat 增强Fatal级别的底色,修改logging类的方法 2024-09-21 11:45:41 +08:00
24 changed files with 931 additions and 336 deletions

92
.clang-tidy Normal file
View File

@@ -0,0 +1,92 @@
---
Checks: '-*,
clang-analyzer-core.*,
clang-analyzer-cplusplus.*,
modernize-redundant-void-arg,
modernize-use-bool-literals,
modernize-use-equals-default,
modernize-use-nullptr,
modernize-use-override,
google-explicit-constructor,
google-readability-casting,
readability-braces-around-statements,
readability-identifier-naming.ClassCase,
readability-identifier-naming.StructCase,
readability-identifier-naming.TypedefCase,
readability-identifier-naming.EnumCase,
readability-non-const-parameter,
cert-dcl21-cpp,
bugprone-undelegated-constructor,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-forward-declaration-namespace,
bugprone-bool-pointer-implicit-conversion,
bugprone-misplaced-widening-cast,
cppcoreguidelines-narrowing-conversions,
misc-unconventional-assign-operator,
misc-unused-parameters'
WarningsAsErrors: ''
HeaderFilterRegex: ''
CheckOptions:
# 现代化Modernize
- key: modernize-redundant-void-arg
value: 'true' # 检查并移除函数声明中冗余的 void 参数。
- key: modernize-use-bool-literals
value: 'true' # 建议使用布尔字面量 true 和 false 代替整数值 0 和 1。
- key: modernize-use-equals-default
value: 'true' # 建议在默认构造函数、复制构造函数和赋值运算符中使用 = default以简化代码。
- key: modernize-use-nullptr
value: 'true' # 建议使用 nullptr 代替 NULL 或 0 来表示空指针。
- key: modernize-use-override
value: 'true' # 建议在覆盖基类虚函数时使用 override 关键字,以增加代码的清晰性和安全性。
# Google 代码风格Google
- key: google-explicit-constructor
value: 'true' # 检查并建议在单参数构造函数中使用 explicit 关键字,以防止隐式转换。
- key: google-readability-casting
value: 'true' # 检查并建议使用 C++ 风格的类型转换(如 static_cast、dynamic_cast、const_cast 和 reinterpret_cast代替 C 风格的类型转换。
# 可读性Readability
- key: readability-braces-around-statements
value: 'true' # 建议在单行语句周围添加大括号,以提高代码的可读性和一致性。
- key: readability-identifier-naming.ClassCase
value: 'CamelCase' # 类名应使用 CamelCase 风格,例如 MyClassName。
- key: readability-identifier-naming.StructCase
value: 'CamelCase' # 结构体名应使用 CamelCase 风格,例如 MyStructName。
- key: readability-identifier-naming.TypedefCase
value: 'CamelCase' # 类型定义应使用 CamelCase 风格,例如 MyTypeDef。
- key: readability-identifier-naming.EnumCase
value: 'CamelCase' # 枚举名应使用 CamelCase 风格,例如 MyEnumName。
- key: readability-non-const-parameter
value: 'true' # 检查并标识非 const 参数,以提高代码的可读性和安全性。
# CERT 安全编码标准CERT
- key: cert-dcl21-cpp
value: 'true' # 检查并标识在头文件中不应包含无命名空间的 using 声明和指令,以防止命名空间污染。
# Bug 检测Bugprone
- key: bugprone-undelegated-constructor
value: 'true' # 检查并标识未委托的构造函数,以确保构造函数的正确性。
- key: bugprone-macro-parentheses
value: 'true' # 检查并建议在宏定义中使用括号,以防止潜在的错误。
- key: bugprone-macro-repeated-side-effects
value: 'true' # 检查并标识宏中重复的副作用,以防止潜在的错误。
- key: bugprone-forward-declaration-namespace
value: 'true' # 检查并标识命名空间前向声明的潜在问题。
- key: bugprone-bool-pointer-implicit-conversion
value: 'true' # 检查并标识布尔指针的隐式转换,以防止潜在的错误。
- key: bugprone-misplaced-widening-cast
value: 'true' # 检查并标识错误的宽化转换,以防止潜在的错误。
# C++ 核心指南CppCoreGuidelines
- key: cppcoreguidelines-narrowing-conversions
value: 'true' # 检查并标识可能导致数据丢失的窄化转换。
# 杂项Miscellaneous
- key: misc-unconventional-assign-operator
value: 'true' # 检查并标识不常见的赋值操作符重载,以确保代码的一致性和可维护性。
- key: misc-unused-parameters
value: 'true' # 检测未使用的参数。
...

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.12

View File

@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.28...3.30)
project(logging)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_CLANG_TIDY "clang-tidy")
if(MSVC)
add_compile_options("/source-charset:utf-8")
@@ -24,3 +25,9 @@ if (TEST)
enable_testing()
add_subdirectory(tests)
endif()
install(FILES include/logging.h DESTINATION include)
install(FILES include/logging/logging-core.h DESTINATION include/logging)
install(FILES include/logging/logging-filter.h DESTINATION include/logging)
install(FILES include/logging/logging-handler.h DESTINATION include/logging)

View File

@@ -32,13 +32,11 @@ cmake --install .
#include "logging.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
@@ -51,14 +49,14 @@ int main() {
#include "logging/logging-handler.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
logger->addHandler(loggingHandlerFile("test1", 1024*1024));
log_Handler *hander = loggingHandlerFile("test_log", 1024 * 1024 * 10);
addHandler(getDefaultLogger(), hander);
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
@@ -69,92 +67,51 @@ int main() {
> Support adding custom filters, currently with built-in substring filters
> The function of an filter is to redirect filtered logs to the filter's dedicated processor
#### example
Redirects filtered logs to a dedicated file processor
```c
#include "logging.h"
#include <stdio.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
char *test1[] = {"123", "tt", NULL};
log_filter *tint = loggingFilterSubStr(
test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
true);
logger->addFilter(tint);
printf("\n");
printf("filter added\n");
printf("\n");
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```
#### Multiple substring filters
```c
#include "logging.h"
#include "logging/logging-core.h"
#include "logging/logging-filter.h"
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
char *test1[] = {"This", NULL};
char *test1[] = {"This",NULL};
log_filter *tint =
loggingFilterSubStr(test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
false);
log_filter *tint = loggingFilterSubStr(
test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
false);
addFilter(getDefaultLogger(), tint);
logger->addFilter(tint);
char *test2[] = {"123",NULL};
char *test2[] = {"123", NULL};
log_filter *tint1 = loggingFilterSubStr(
test2,
LOG_DEBUG,
LOG_ERROR,
loggingHandlerFile("test_interceptor1", 1024 * 1024),
true);
logger->addFilter(tint1);
addFilter(getDefaultLogger(), tint1);
printf("\n");
printf("filter added\n");
printf("\n");
log_info("This is an info message");
log_error("This is an error message%s", "123");
log_fatal("This is an fatal message");
log_debug("This is a debug message");
log_warning("This is a warning message%s", "123");
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;

View File

@@ -47,8 +47,6 @@ cmake --install .
#include "logging.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
@@ -66,8 +64,8 @@ int main() {
#include "logging/logging-handler.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
logger->addHandler(loggingHandlerFile("test1", 1024*1024));
log_Handler *hander = loggingHandlerFile("test_log", 1024 * 1024 * 10);
addHandler(getDefaultLogger(), hander);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
@@ -85,82 +83,41 @@ int main() {
> 过滤器的作用:可以将过滤到的日志重定向到过滤器的专属处理器中
#### 单个子串过滤器
将过滤到的日志重定向到专属处理器中
```c
#include "logging.h"
#include <stdio.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
char *test1[] = {"123", "tt", NULL};
log_filter *tint = loggingFilterSubStr(
test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
true);
logger->addFilter(tint);
printf("\n");
printf("filter added\n");
printf("\n");
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
destroyDefaultLogger();
return 0;
}
```
#### 多个子串过滤器
```c
#include "logging.h"
#include "logging/logging-core.h"
#include "logging/logging-filter.h"
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
Log_debug("This is a debug message");
Log_warning("This is a warning message%s", "123");
char *test1[] = {"This",NULL};
char *test1[] = {"This", NULL};
log_filter *tint = loggingFilterSubStr(
test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
false);
log_filter *tint =
loggingFilterSubStr(test1,
LOG_DEBUG,
loggingHandlerFile("test_interceptor", 1024 * 1024),
false);
logger->addFilter(tint);
addFilter(getDefaultLogger(), tint);
char *test2[] = {"123",NULL};
char *test2[] = {"123", NULL};
log_filter *tint1 = loggingFilterSubStr(
test2,
LOG_DEBUG,
LOG_ERROR,
loggingHandlerFile("test_interceptor1", 1024 * 1024),
true);
logger->addFilter(tint1);
addFilter(getDefaultLogger(), tint1);
printf("\n");
printf("filter added\n");

View File

@@ -5,7 +5,7 @@ import os
class loggingRecipe(ConanFile):
name = "logging"
version = "0.5.1"
version = "1.0.0"
license = "MIT"
author = "321640253@qq.com"
url = "https://github.com/WangZhongDian/logging.git"

View File

@@ -3,6 +3,7 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include "logging/logging-core.h"
#include "logging/logging-filter.h"
@@ -12,51 +13,72 @@
extern "C" {
#endif
// 默认日志器宏
#define Log_fatal(format, ...) \
log_fatal(__FILE__, __LINE__, format, ##__VA_ARGS__)
logMessage(NULL, LOG_FATAL, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define Log_error(format, ...) \
log_error(__FILE__, __LINE__, format, ##__VA_ARGS__)
logMessage(NULL, LOG_ERROR, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define Log_warning(format, ...) \
log_warning(__FILE__, __LINE__, format, ##__VA_ARGS__)
logMessage(NULL, LOG_WARNING, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define Log_info(format, ...) \
log_info(__FILE__, __LINE__, format, ##__VA_ARGS__)
logMessage(NULL, LOG_INFO, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define Log_debug(format, ...) \
log_debug(__FILE__, __LINE__, format, ##__VA_ARGS__)
logMessage(NULL, LOG_DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__)
// 日志器宏
#define log_fatal(logger, format, ...) \
logMessage(logger, LOG_FATAL, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define log_error(logger, format, ...) \
logMessage(logger, LOG_ERROR, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define log_warning(logger, format, ...) \
logMessage(logger, LOG_WARNING, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define log_info(logger, format, ...) \
logMessage(logger, LOG_INFO, __FILE__, __LINE__, format, ##__VA_ARGS__)
#define log_debug(logger, format, ...) \
logMessage(logger, LOG_DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__)
typedef struct Logger {
log_level level;
log_Handler *handler;
log_filter *filter;
const char *name;
bool (*addHandler)(log_Handler *handler);
bool (*addFilter)(log_filter *filter);
} Logger;
void log_fatal(const char *file, int line, const char *format, ...);
void log_error(const char *file, int line, const char *format, ...);
void log_warning(const char *file, int line, const char *format, ...);
void log_info(const char *file, int line, const char *format, ...);
void log_debug(const char *file, int line, const char *format, ...);
bool addHandler(Logger *logger, log_Handler *handler);
bool addFilter(Logger *logger, log_filter *filter);
void logMessage(Logger *logger,
log_level level,
const char *file,
int line,
const char *message,
...);
/**
* @brief
创建默认日志对象,日志对象为单例模式后续可通过getDefaultLogger方法获取
重复调用该方法不会创建新的日志对象,只会返回默认日志对象,并且会修改默认日志对象的名称和等级
* @param name 日志名称
* @param level 日志等级
* @return Logger* 日志对象指针
*/
Logger *newDefaultLogger(const char *name, log_level level);
* @brief 创建一个日志句柄对象
* @param name 日志器名称
* @return 日志句柄对象
*/
Logger *newLogger(const char *name);
/**
* @brief 获取默认日志对象
* @return 默认日志对象
*/
Logger *getDefaultLogger(void);
Logger *getLogger(const char *name);
/**
* @brief 销毁日志对象,该方法会销毁默认日志对象
*/
log_status destroyDefaultLogger(void);
void destroyDefaultLogger(void);
/**
* @brief 销毁日志对象
* @param logger 日志对象
* @return void
*/
void destroyLogger(Logger *logger);
#ifdef __cplusplus
}

View File

@@ -13,6 +13,14 @@ typedef enum {
LOG_DEBUG,
} log_level;
static const char *LOG_LEVEL_STR[] = {
"FATAL",
"ERROR",
"WARNING",
"INFO",
"DEBUG",
};
typedef enum {
L_ERROR,
L_OK,

View File

@@ -16,11 +16,11 @@ typedef struct log_Handler {
/**
* @brief 文件处理器
* @param name 文件名
* @param file_name 文件名
* @param max_size 文件最大大小
* @return
*/
log_Handler *loggingHandlerFile(const char *name, unsigned int max_size);
log_Handler *loggingHandlerFile(const char *file_name, unsigned int max_size);
/**
* @brief 控制台处理器

View File

@@ -3,4 +3,14 @@
.PHONY:format
format:
bash script/format.sh
bash script/format.sh
build:
cmake -S . -B build
cmake --build build
test: build
cd build && ctest
clean:
rm -rf build

9
pyproject.toml Normal file
View File

@@ -0,0 +1,9 @@
[project]
name = "logging"
version = "1.0.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"conan>=2.18.1",
]

View File

@@ -11,3 +11,4 @@ else()
add_library(${PROJECT_NAME} ${SRC})
endif()
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION lib)

View File

@@ -24,15 +24,17 @@ static void get_next(char *str, int *next) {
}
static bool kmp_search(char *substr, char *master) {
if (substr == NULL)
if (substr == NULL) {
return true; // 空串全匹配
if (master == NULL)
}
if (master == NULL) {
return false;
int i = 0;
int j = 0;
int substrlen = strlen(substr);
int masterlen = strlen(master);
int *next = (int *)malloc(sizeof(int) * (substrlen + 1));
}
int i = 0;
int j = 0;
size_t substrlen = strlen(substr);
size_t masterlen = strlen(master);
int *next = (int *)malloc(sizeof(int) * (substrlen + 1));
get_next(substr, next);
while (i < masterlen && j < substrlen) {
@@ -49,10 +51,11 @@ static bool kmp_search(char *substr, char *master) {
}
free(next);
if (j == substrlen)
if (j == substrlen) {
return true;
else
} else {
return false;
}
}
static bool _disposeSubstring(log_filter *filter,
@@ -63,14 +66,16 @@ static bool _disposeSubstring(log_filter *filter,
keywords_t *keyword = (keywords_t *)(filter + 1);
if (keyword->key == NULL && keyword->next == NULL) {
if (level <= filter->level)
if (level <= filter->level) {
return true;
}
return false;
}
while (keyword != NULL && level <= filter->level) {
if (kmp_search(keyword->key, (char *)message))
if (kmp_search(keyword->key, (char *)message)) {
return true;
}
keyword = keyword->next;
}
@@ -94,8 +99,9 @@ static void _freeFilter(log_filter *filter) {
filter->handler->_free(filter->handler);
}
if (it_keyword->key != NULL)
if (it_keyword->key != NULL) {
free(it_keyword->key);
}
free(filter);
}

View File

@@ -7,7 +7,7 @@
// log_Handler_file_ex_t与log_Handler处于连续内存中
// 使用char*指针进行偏移达到以偏移1个字节为单位的偏移
#define Handler_file_EX_PRT(handler) \
((log_Handler_file_ex_t *)((char *)handler + sizeof(log_Handler)))
((log_Handler_file_ex_t *)((char *)(handler) + sizeof(log_Handler)))
#define FILE_NAME_MAX_SIZE 50
@@ -46,11 +46,12 @@ static void outputFileHandler(log_Handler *handler, const char *message) {
fputs(message, handler->stream);
log_Handler_file_ex_t *handler_ex = Handler_file_EX_PRT(handler);
handler_ex->file_size += strlen(message);
if (handler_ex->file_size > handler_ex->file_size_max)
if (handler_ex->file_size > handler_ex->file_size_max) {
changeFile(handler);
}
}
log_Handler *loggingHandlerFile(const char *name, unsigned int max_size) {
log_Handler *loggingHandlerFile(const char *file_name, unsigned int max_size) {
char new_file_name[FILE_NAME_MAX_SIZE];
int suffix = 0;
unsigned int file_size;
@@ -60,26 +61,29 @@ log_Handler *loggingHandlerFile(const char *name, unsigned int max_size) {
/// 获取未写满于设置最大文件大小的文件名
do {
sprintf(new_file_name, "%s_%d.log", name, suffix++);
sprintf(new_file_name, "%s_%d.log", file_name, suffix++);
fp = fopen(new_file_name, "at");
if (fp == NULL)
if (fp == NULL) {
goto ERROR;
}
file_size = getFileSize(fp);
} while (file_size > max_size);
/// 分配log_Handler与记录文件大小的空间
handler = (log_Handler *)malloc(sizeof(log_Handler) +
sizeof(log_Handler_file_ex_t));
if (handler == NULL)
if (handler == NULL) {
goto ERROR;
}
handler_ex = Handler_file_EX_PRT(handler);
handler_ex->file_size_max = max_size;
handler_ex->file_size = file_size;
handler_ex->suffix = suffix;
handler_ex->file_name = strdup(name);
if (handler_ex->file_name == NULL)
handler_ex->file_name = strdup(file_name);
if (handler_ex->file_name == NULL) {
goto ERROR;
}
handler->stream = fp;
handler->apply_color = false;
@@ -88,8 +92,9 @@ log_Handler *loggingHandlerFile(const char *name, unsigned int max_size) {
return handler;
ERROR:
if (fp)
if (fp) {
fclose(fp);
}
if (handler) {
free(Handler_file_EX_PRT(handler)->file_name); // 直接释放无需检查NULL
free(handler);

View File

@@ -1,6 +1,7 @@
#include "logging.h"
#include "logging/logging-core.h"
#include "logging/logging-handler.h"
#include "utils/logging-map.h"
#include "utils/logging-utils.h"
#include <stdbool.h>
#include <stdio.h>
@@ -18,23 +19,25 @@
#define LOG_BUFFER_SIZE 4096 // 日志缓冲区大小,单个日志长度不能超过该值
static Logger *G_LOGGER = NULL; // 全局日志对象,唯一实例
static Logger *ROOT_LOGGER = NULL; // 日志对象,唯一实例
static Map *LOGGER_MAP = NULL; // 日志对象映射表
/**
* @brief 为日志添加一个handler
* @param handler 处理器对象
*/
static bool addHandler(log_Handler *handler) {
if (G_LOGGER == NULL || handler == NULL) {
bool addHandler(Logger *logger, log_Handler *handler) {
if (logger == NULL || handler == NULL) {
return false;
}
if (G_LOGGER->handler == NULL) {
G_LOGGER->handler = handler;
if (logger->handler == NULL) {
logger->handler = handler;
return true;
}
G_LOGGER->handler->_free(G_LOGGER->handler);
G_LOGGER->handler = handler;
logger->handler->_free(logger->handler);
logger->handler = handler;
return true;
}
@@ -42,17 +45,17 @@ static bool addHandler(log_Handler *handler) {
* @brief 为日志添加一个filter
* @param filter 过滤器对象
*/
static bool addFilter(log_filter *filter) {
if (G_LOGGER == NULL || filter == NULL) {
bool addFilter(Logger *logger, log_filter *filter) {
if (logger == NULL || filter == NULL) {
return false;
}
if (G_LOGGER->filter == NULL) {
G_LOGGER->filter = filter;
G_LOGGER->filter->next = NULL;
if (logger->filter == NULL) {
logger->filter = filter;
logger->filter->next = NULL;
return true;
}
log_filter *it = G_LOGGER->filter;
log_filter *it = logger->filter;
while (it->next != NULL) {
it = it->next;
}
@@ -69,33 +72,42 @@ static bool addFilter(log_filter *filter) {
* @param color 应用的颜色
* @param message 日志内容
*/
static void output_to_handler(log_Handler *handler,
char *level,
const char *color,
const char *message) {
/**
* @brief 输出到handler
* @param handler 处理器对象
* @param level 日志等级
* @param color 应用的颜色
* @param message 日志内容
*/
static void output_to_handler(Logger *logger,
char *level,
const char *color,
const char *message) {
char timeStr[20];
getTimeStr(timeStr);
char logStr[LOG_BUFFER_SIZE * 2];
if (handler->apply_color)
if (logger->handler->apply_color) {
snprintf(logStr,
LOG_BUFFER_SIZE * 2,
"[%s]: %s %s%s%s %s\n",
G_LOGGER->name,
logger->name,
timeStr,
color,
level,
RESET,
message);
else
} else {
snprintf(logStr,
LOG_BUFFER_SIZE * 2,
"[%s]: %s %s %s\n",
G_LOGGER->name,
logger->name,
timeStr,
level,
message);
}
handler->output(handler, logStr);
logger->handler->output(logger->handler, logStr);
}
/**
@@ -106,133 +118,153 @@ static void output_to_handler(log_Handler *handler,
* @param ... 格式化参数列表
* @return
*/
static void _builtin_cope(log_level level_e,
char *level,
const char *color,
const char *message) {
if (G_LOGGER == NULL) {
static void
log_cope(Logger *logger, char *level, const char *color, const char *message) {
if (logger == NULL) {
return;
}
if (G_LOGGER->handler == NULL) {
if (logger->handler == NULL) {
return;
}
log_filter *it = G_LOGGER->filter;
log_Handler *handler = G_LOGGER->handler;
log_filter *it = logger->filter;
log_Handler *handler = logger->handler;
while (it != NULL) {
if (it->_dispose(it, level_e, message)) {
output_to_handler(it->handler, level, color, message);
if (it->jump_out)
if (it->_dispose(it, logger->level, message)) {
output_to_handler(logger, level, color, message);
if (it->jump_out) {
return;
}
}
it = it->next;
}
output_to_handler(handler, level, color, message);
output_to_handler(logger, level, color, message);
}
void log_fatal(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_ERROR) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
void logMessage(Logger *logger,
log_level level,
const char *file,
int line,
const char *message,
...) {
Logger *_logger = NULL;
if (logger == NULL) {
if (ROOT_LOGGER == NULL) {
ROOT_LOGGER = newLogger("ROOT"); // 创建根日志对象
}
_logger = ROOT_LOGGER;
} else {
_logger = logger; // 使用传入的日志对象
}
if (_logger->level >= level) {
char logStr
[LOG_BUFFER_SIZE]; // 定义一个缓冲区,用于存储格式化后的日志字符串
char finalLogStr[LOG_BUFFER_SIZE *
2]; // 定义一个缓冲区,用于存储最终输出的日志字符串
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_FATAL, "Fatal", RED_B, finalLogStr);
switch (level) {
case LOG_DEBUG:
log_cope(_logger, "Debug", BLUE, finalLogStr);
break;
case LOG_INFO:
log_cope(_logger, "Info", GREEN, finalLogStr);
break;
case LOG_WARNING:
log_cope(_logger, "Warning", YELLOW, finalLogStr);
break;
case LOG_ERROR:
log_cope(_logger, "Error", RED, finalLogStr);
break;
case LOG_FATAL:
log_cope(_logger, "Fatal", RED_B, finalLogStr);
break;
default:
break;
}
}
}
void log_error(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_ERROR) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_ERROR, "Error", RED, finalLogStr);
}
}
void log_warning(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_WARNING) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_WARNING, "Warning", YELLOW, finalLogStr);
}
}
void log_info(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_INFO) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_INFO, "Info", GREEN, finalLogStr);
}
}
void log_debug(const char *file, int line, const char *message, ...) {
if (G_LOGGER->level >= LOG_DEBUG) {
char logStr[LOG_BUFFER_SIZE];
char finalLogStr[LOG_BUFFER_SIZE * 2];
va_list args;
va_start(args, message);
vsprintf(logStr, message, args);
va_end(args);
snprintf(
finalLogStr, LOG_BUFFER_SIZE * 2, "[%s:%d] %s", file, line, logStr);
_builtin_cope(LOG_DEBUG, "Debug", CYAN, finalLogStr);
}
}
Logger *newDefaultLogger(const char *name, log_level level) {
if (G_LOGGER != NULL) {
G_LOGGER->name = name;
G_LOGGER->level = level;
return G_LOGGER;
}
Logger *logger = (Logger *)malloc(sizeof(Logger));
logger->addHandler = addHandler;
logger->addFilter = addFilter;
logger->level = level;
logger->handler = loggingHandlerConsole();
logger->name = name;
logger->filter = NULL;
G_LOGGER = logger;
return G_LOGGER;
}
/**
* @brief 销毁日志对象
* @brief 创建一个日志句柄
* @param name 日志器名称
* @return 日志器对象
*/
log_status destroyDefaultLogger(void) {
if (G_LOGGER != NULL) {
if (G_LOGGER->handler != NULL) {
G_LOGGER->handler->_free(G_LOGGER->handler);
Logger *newLogger(const char *name) {
Logger *logger = (Logger *)malloc(sizeof(Logger));
if (logger == NULL) {
return NULL;
}
if (LOGGER_MAP == NULL) {
LOGGER_MAP = map_create(sizeof(Logger **));
}
logger->level = LOG_INFO;
logger->handler = loggingHandlerConsole();
logger->name = name;
logger->filter = NULL;
map_put(LOGGER_MAP, name, &logger);
return logger;
}
Logger *getDefaultLogger(void) {
if (ROOT_LOGGER != NULL) {
return ROOT_LOGGER;
}
ROOT_LOGGER = newLogger("ROOT");
return ROOT_LOGGER;
}
/**
* @brief 获取日志器对象
* @param name 日志器名称
* @param level 日志等级
* @return 日志器对象
*/
Logger *getLogger(const char *name) {
if (name == NULL) {
return NULL;
}
if (LOGGER_MAP == NULL) {
LOGGER_MAP = map_create(sizeof(Logger **));
}
Logger **cache_logger_ptr = (Logger **)map_get(LOGGER_MAP, name);
if (cache_logger_ptr != NULL) {
return *cache_logger_ptr;
}
Logger *logger = (Logger *)malloc(sizeof(Logger));
logger->level = LOG_INFO;
logger->handler = loggingHandlerConsole();
logger->name = name;
logger->filter = NULL;
map_put(LOGGER_MAP, name, &logger);
return logger;
}
void destroyLogger(Logger *logger) {
if (logger != NULL) {
if (logger->handler != NULL) {
logger->handler->_free(logger->handler);
}
if (G_LOGGER->filter != NULL) {
log_filter *it = G_LOGGER->filter;
if (logger->filter != NULL) {
log_filter *it = logger->filter;
log_filter *next = NULL;
while (it != NULL) {
next = it->next;
@@ -240,16 +272,21 @@ log_status destroyDefaultLogger(void) {
it = next;
}
}
free(G_LOGGER);
G_LOGGER = NULL;
free(logger);
}
return L_OK;
}
Logger *getDefaultLogger(void) {
if (G_LOGGER == NULL) {
return NULL;
}
return G_LOGGER;
static void
__destroyLoggerForeach(const char *key, void *value, void *user_data) {
(void)user_data;
(void)key;
destroyLogger(*(Logger **)value);
}
/**
* @brief 销毁日志对象
*/
void destroyDefaultLogger(void) {
map_foreach(LOGGER_MAP, __destroyLoggerForeach, NULL);
map_destroy(LOGGER_MAP);
}

143
src/utils/logging-map.c Normal file
View File

@@ -0,0 +1,143 @@
#include "logging-map.h"
#include <stdlib.h>
#include <string.h>
/* 哈希函数FNV-1a */
static uint32_t hash_bytes(const void *data, size_t len) {
const uint8_t *p = data;
uint32_t h = 2166136261u;
while (len--) {
h ^= *p++;
h *= 16777619u;
}
return h;
}
/* 创建空 map */
Map *map_create(size_t value_len) {
Map *m = calloc(1, sizeof(*m));
m->value_len = value_len;
m->bucket_cap = 8; /* 初始桶数 */
m->bucket = calloc(m->bucket_cap, sizeof(Node *));
return m;
}
/* 根据 key 找到桶下标 */
static size_t bucket_index(Map *m, const char *key) {
size_t len = strlen(key);
uint32_t h = hash_bytes(key, len);
return h & (m->bucket_cap - 1); /* 要求 bucket_cap 是 2 的幂 */
}
/* 在桶链中线性查找 */
static Node *find_in_chain(Node *head, const char *key) {
for (; head; head = head->next) {
if (strcmp(head->key, key) == 0) {
return head;
}
}
return NULL;
}
/* 扩容2× 桶数,重新哈希所有节点 */
static void map_resize(Map *m) {
size_t new_cap = m->bucket_cap * 2;
Node **new_bucket = calloc(new_cap, sizeof(Node *));
for (size_t i = 0; i < m->bucket_cap; ++i) {
Node *node = m->bucket[i];
while (node) {
Node *next = node->next;
size_t idx =
hash_bytes(node->key, strlen(node->key)) & (new_cap - 1);
node->next = new_bucket[idx];
new_bucket[idx] = node;
node = next;
}
}
free(m->bucket);
m->bucket = new_bucket;
m->bucket_cap = new_cap;
}
/* 插入或覆盖 */
void map_put(Map *m, const char *key, const void *value) {
if (m->size * 4 >= m->bucket_cap * 3) { /* 装载因子 0.75 */
map_resize(m);
}
size_t idx = bucket_index(m, key);
Node *node = find_in_chain(m->bucket[idx], key);
if (node) { /* 覆盖旧值 */
memcpy(node->value, value, m->value_len);
return;
}
/* 新建节点 */
node = malloc(sizeof(*node));
node->value = malloc(m->value_len);
node->next = m->bucket[idx];
node->key = malloc(strlen(key) + 1);
memcpy(node->key, key, strlen(key) + 1);
memcpy(node->value, value, m->value_len);
m->bucket[idx] = node;
++m->size;
}
/* 查找 */
void *map_get(Map *m, const char *key) {
size_t idx = bucket_index(m, key);
Node *node = find_in_chain(m->bucket[idx], key);
return node ? node->value : NULL;
}
/* 删除 */
bool map_erase(Map *m, const char *key) {
size_t idx = bucket_index(m, key);
Node **link = &m->bucket[idx];
for (; *link; link = &(*link)->next) {
if (strcmp((*link)->key, key) == 0) {
Node *to_del = *link;
*link = to_del->next;
free(to_del->key);
free(to_del->value);
free(to_del);
--m->size;
return true;
}
}
return false;
}
/* 销毁 */
void map_destroy(Map *m) {
for (size_t i = 0; i < m->bucket_cap; ++i) {
Node *node = m->bucket[i];
while (node) {
Node *next = node->next;
free(node->key);
free(node->value);
free(node);
node = next;
}
}
free(m->bucket);
free(m);
}
/* 遍历 */
void map_foreach(Map *m,
void (*callback)(const char *key,
void *value,
void *user_data),
void *user_data) {
for (size_t i = 0; i < m->bucket_cap; ++i) {
Node *node = m->bucket[i];
while (node) {
callback(node->key, node->value, user_data);
node = node->next;
}
}
}

45
src/utils/logging-map.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef __LOGGING_MAP_H__
#define __LOGGING_MAP_H__
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef struct Node {
struct Node *next;
char *key;
void *value;
} Node;
struct Map {
size_t value_len;
size_t bucket_cap;
size_t size; /* 当前元素个数 */
Node **bucket; /* 指针数组 */
};
typedef struct Map Map;
/* 创建空 mapkey_len/value_len 以字节为单位 */
Map *map_create(size_t value_len);
/* 插入或覆盖:<key> 必须指向 key_len 字节;同理 value */
void map_put(Map *m, const char *key, const void *value);
/* 查找:找到返回 value 指针;未找到返回 NULL */
void *map_get(Map *m, const char *key);
/* 删除:返回 true 表示确实删掉了 */
bool map_erase(Map *m, const char *key);
/* 遍历: 遍历所有元素*/
void map_foreach(Map *m,
void (*callback)(const char *key,
void *value,
void *user_data),
void *user_data);
/* 销毁并释放所有内存 */
void map_destroy(Map *m);
#endif /* __LOGGING_MAP_H__ */

View File

@@ -2,29 +2,30 @@ project(test)
enable_testing()
include_directories(${CMAKE_SOURCE_DIR}/src)
#测试简单基本应用
add_executable(${PROJECT_NAME}simple test-simple.c)
target_link_libraries(${PROJECT_NAME}simple logging)
if(UNIX)
add_test(test_simple ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}simple)
elseif(WIN32)
add_test(test_simple ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}simple.exe)
endif()
add_test(test_simple ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}simple${CMAKE_EXECUTEABLE_SUFFIX})
#测试简单基本应用
add_executable(${PROJECT_NAME}file test-log-file.c)
target_link_libraries(${PROJECT_NAME}file logging)
if(UNIX)
add_test(test_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}file)
elseif(WIN32)
add_test(test_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}file.exe)
endif()
add_test(test_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}file${CMAKE_EXECUTEABLE_SUFFIX})
#测试拦截器
add_executable(${PROJECT_NAME}filter test-filter.c)
target_link_libraries(${PROJECT_NAME}filter logging)
if(UNIX)
add_test(test_filter ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}filter)
elseif(WIN32)
add_test(test_filter ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}filter.exe)
endif()
add_test(test_filter ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}filter${CMAKE_EXECUTEABLE_SUFFIX})
#测试多log多次获取
add_executable(${PROJECT_NAME}logs test-logs.c)
target_link_libraries(${PROJECT_NAME}logs logging)
add_test(test_logs ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}logs${CMAKE_EXECUTEABLE_SUFFIX})
#测试工具map
add_executable(${PROJECT_NAME}map test-map.c)
target_link_libraries(${PROJECT_NAME}map logging)
add_test(test_map ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}map${CMAKE_EXECUTEABLE_SUFFIX})

View File

@@ -6,8 +6,6 @@
#include <time.h>
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");
@@ -22,7 +20,7 @@ int main() {
loggingHandlerFile("test_interceptor", 1024 * 1024),
false);
logger->addFilter(tint);
addFilter(getDefaultLogger(), tint);
char *test2[] = {"123", NULL};
@@ -32,7 +30,7 @@ int main() {
loggingHandlerFile("test_interceptor1", 1024 * 1024),
true);
logger->addFilter(tint1);
addFilter(getDefaultLogger(), tint1);
printf("\n");
printf("filter added\n");

View File

@@ -2,9 +2,8 @@
#include "logging/logging-handler.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
log_Handler *hander = loggingHandlerFile("test_log", 1024 * 1024 * 10);
logger->addHandler(hander);
addHandler(getDefaultLogger(), hander);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");

24
tests/test-logs.c Normal file
View File

@@ -0,0 +1,24 @@
#include "logging.h"
#include "logging/logging-core.h"
#include <stdio.h>
int main() {
Logger *t1 = getLogger("Test1");
t1->level = LOG_ERROR;
Logger *t2 = getLogger("Test2");
t2->level = LOG_DEBUG;
Logger *t11 = getLogger("Test1");
if (t1 == t11) {
printf("t1 and t11 are the same\n");
printf("t1 log level: %s", LOG_LEVEL_STR[t11->level]);
} else {
printf("t1 and t11 are different\n");
return 1;
}
destroyDefaultLogger();
return 0;
}

37
tests/test-map.c Normal file
View File

@@ -0,0 +1,37 @@
#include "utils/logging-map.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void foreach_callback(const char *key, void *value, void *user_data) {
(void)user_data;
printf("foreach key: %s, value: %d\n", key, *(int *)value);
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Map *map = map_create(sizeof(int));
const char *keys[] = {"key1", "key22", "key33", "key44", "key55"};
for (int i = 0; i < 5; i++) {
printf("put key %s,value %d\n", keys[i], i);
map_put(map, keys[i], &i);
}
for (int i = 0; i < 5; i++) {
int *value = map_get(map, keys[i]);
printf("get value: %d\n", *value);
if (*value != i) {
return 1;
}
}
map_foreach(map, foreach_callback, NULL);
map_destroy(map);
return 0;
}

View File

@@ -1,8 +1,6 @@
#include "logging.h"
int main() {
Logger *logger = newDefaultLogger("testLogger", LOG_DEBUG);
Log_info("This is an info message");
Log_error("This is an error message%s", "123");
Log_fatal("This is an fatal message");

238
uv.lock generated Normal file
View File

@@ -0,0 +1,238 @@
version = 1
revision = 2
requires-python = ">=3.12"
[[package]]
name = "certifi"
version = "2025.6.15"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload-time = "2025-06-15T02:45:51.329Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload-time = "2025-06-15T02:45:49.977Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.2"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "conan"
version = "2.18.1"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "colorama" },
{ name = "distro", marker = "platform_system == 'FreeBSD' or sys_platform == 'linux'" },
{ name = "fasteners" },
{ name = "jinja2" },
{ name = "patch-ng" },
{ name = "python-dateutil" },
{ name = "pyyaml" },
{ name = "requests" },
{ name = "urllib3" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c7/55/9a10f21dd6dc9137c6e7b51eff7c54ab92400812e6e795d1a991ead00b54/conan-2.18.1.tar.gz", hash = "sha256:5d8e9fac7614de9297933f65de8f17db14851a871cebc962f4856b7c294f43c5", size = 521034, upload-time = "2025-07-04T07:08:07.842Z" }
[[package]]
name = "distro"
version = "1.8.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/89/eaa3a3587ebf8bed93e45aa79be8c2af77d50790d15b53f6dfc85b57f398/distro-1.8.0.tar.gz", hash = "sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8", size = 59428, upload-time = "2022-10-10T15:30:33.395Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/2c/c90a3adaf0ddb70afe193f5ebfb539612af57cffe677c3126be533df3098/distro-1.8.0-py3-none-any.whl", hash = "sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff", size = 20315, upload-time = "2022-10-10T15:30:26.903Z" },
]
[[package]]
name = "fasteners"
version = "0.19"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/d4/e834d929be54bfadb1f3e3b931c38e956aaa3b235a46a3c764c26c774902/fasteners-0.19.tar.gz", hash = "sha256:b4f37c3ac52d8a445af3a66bce57b33b5e90b97c696b7b984f530cf8f0ded09c" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/bf/fd60001b3abc5222d8eaa4a204cd8c0ae78e75adc688f33ce4bf25b7fafa/fasteners-0.19-py3-none-any.whl", hash = "sha256:758819cb5d94cdedf4e836988b74de396ceacb8e2794d21f82d131fd9ee77237" },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
]
[[package]]
name = "jinja2"
version = "3.1.6"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
]
[[package]]
name = "logging"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "conan" },
]
[package.metadata]
requires-dist = [{ name = "conan", specifier = ">=2.18.1" }]
[[package]]
name = "markupsafe"
version = "3.0.2"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
]
[[package]]
name = "patch-ng"
version = "1.18.1"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/c0/53a2f017ac5b5397a7064c2654b73c3334ac8461315707cbede6c12199eb/patch-ng-1.18.1.tar.gz", hash = "sha256:52fd46ee46f6c8667692682c1fd7134edc65a2d2d084ebec1d295a6087fc0291", size = 17913, upload-time = "2024-10-25T12:20:10.591Z" }
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.2"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873, upload-time = "2024-08-06T20:32:25.131Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302, upload-time = "2024-08-06T20:32:26.511Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154, upload-time = "2024-08-06T20:32:28.363Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223, upload-time = "2024-08-06T20:32:30.058Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542, upload-time = "2024-08-06T20:32:31.881Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164, upload-time = "2024-08-06T20:32:37.083Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611, upload-time = "2024-08-06T20:32:38.898Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591, upload-time = "2024-08-06T20:32:40.241Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338, upload-time = "2024-08-06T20:32:41.93Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309, upload-time = "2024-08-06T20:32:43.4Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679, upload-time = "2024-08-06T20:32:44.801Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428, upload-time = "2024-08-06T20:32:46.432Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361, upload-time = "2024-08-06T20:32:51.188Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523, upload-time = "2024-08-06T20:32:53.019Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660, upload-time = "2024-08-06T20:32:54.708Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" },
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" },
]
[[package]]
name = "requests"
version = "2.32.4"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258, upload-time = "2025-06-09T16:43:07.34Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" },
]
[[package]]
name = "six"
version = "1.17.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "urllib3"
version = "2.0.7"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/af/47/b215df9f71b4fdba1025fc05a77db2ad243fa0926755a52c5e71659f4e3c/urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", size = 282546, upload-time = "2023-10-17T17:46:50.542Z" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/d2/b2/b157855192a68541a91ba7b2bbcb91f1b4faa51f8bae38d8005c034be524/urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e", size = 124213, upload-time = "2023-10-17T17:46:48.538Z" },
]