轻量级训练监控库:多 GPU 指标合并 + 控制台实时进度条 + Web 可视化。
epoch 3 loss=0.2153 acc=0.8974 lr=8.2e-04 ━━━━━━━━━━━────────── 45.0% [90/200] [0:03<0:04 30.0it/s]
特性
- 控制台实时进度条:自研 tqdm(用法与 tqdm.tqdm 一致),
[n/total]领先、指标紧随其后实时刷新;终端下自适应列宽渲染——内容不超行、绝不自动换行:进度条吃掉固定段之外的全部剩余列恰好占满整行,剩余不足时收缩到最小宽度,仍放不下的指标以省略号收尾(重定向 / 日志文件无列宽概念,按固定宽度渲染);进度条与 print 同步镜像到每次会话独立的console-<时间戳>.log(run 目录已有会话产物时新文件自动加序号前缀2.、3.……,metrics 日志同理;进度条行就地实时刷新、带时间戳前缀,与终端内容一致,编辑器打开可看到动的进度条) - 多 GPU 指标合并:基于
torch.distributedall_reduce 跨进程聚合(默认取均值),仅 rank0 记录与展示;未装 torch 自动退化单进程。StepsBar(reduce=False)可关闭合并(训练期间只看 master 实时值,零集合通信) - Web 可视化:FastAPI + WebSocket + ECharts,后台线程运行,实时推送曲线,断线自动重连;支持
tab分区(train / val / test 垂直分块,各分区 step 独立计数)与层级命名多系列卡片(多分类逐类曲线一图对比) - 持久化:指标写入自研二进制容器(符号表 + varint 增量编码,体积约为 JSONL 的 1/4),每次启动一个带时间戳的会话文件,互不覆盖
- 断点续训:重跑同一
log_dir自动接续历史曲线;从某个 epoch 重新训练时自动清除上次中断留下的重叠数据,折线不会在 x 轴上回退
安装
pip install melog # 基础安装,来自 PyPI
多 GPU 合并基于 torch.distributed,假定环境中已装好 PyTorch;未装 torch 时自动退化单进程。
快速开始
import melog
from melog import StepsBar
melog.init("runs/my-exp") # 日志保存路径;端口缺省自动选空闲端口
# Web 地址启动时自动打印,也可读 melog.current().web_url
for step in StepsBar(range(1000)): # tqdm 风格:自动推进,无需手动 update
loss = train_one_step()
melog.scalar({"loss": loss, "lr": 1e-3}) # 记录 + 刷新进度条指标 + 推送 Web
训练期间浏览器打开启动时打印的 Web 地址(即 melog.current().web_url)查看实时曲线。
全局共享
melog.init 是唯一入口,创建的实例自动成为全局活动实例。入口处 init 一次,
项目任何地方直接用模块级接口,无需层层传递实例:
import melog
melog.init(log_dir="runs/my-exp") # 日志保存路径;端口缺省自动选空闲端口
# 任意其他模块中:
import melog
melog.scalar({"loss": 0.5})
melog.image("sample", img)
# 收尾:进程退出时自动完成,无需调用
log_dir末级目录名即项目名(runs/my-exp→ 项目my-exp),本次运行落在runs/my-exp/<时间戳>/下;project=可覆盖项目名- 最近一次创建的实例即全局活动实例(
melog.current()取回),收尾后清空 - 进程退出时经 atexit 自动收尾:落盘剩余指标、定稿进度条、停 Web、还原 print, 无需任何手动调用
- 模块级
scalar / image / audio / log / success / error / warn / set_colors / current_bar与实例方法等价 - 实例内部有锁,多线程 / 多模块共享安全;多 GPU 约定不变
曲线上体现 epoch
本库按 epoch 组织训练记录:每个 epoch 的循环必须用 StepsBar 包裹并传入
epoch,坐标(epoch / step)由它统一管理——scalar() / image() /
audio() 都没有坐标参数,记录自动依附当前 epoch 与下一个空槽:
from melog import StepsBar
for epoch in range(epochs):
for _ in StepsBar(loader, epoch=epoch): # 行首自动标注 "epoch N"
loss = train_one_step()
melog.scalar({"loss": loss, "lr": lr}) # 坐标自动依附当前 epoch
StepsBar(epoch=...)进入进度条即绑定 epoch:epoch 内步数清零、全局 x 从上一位置 接续;bar 结束后沿用绑定值,直至下一个 epochstep为当前 epoch 内的记录序号,内部自增(每个 epoch 从 0 重新计步); 完全没用StepsBar(epoch=...)时退化为全局自增 x、不标注 epoch 分界- 要控制记录粒度(每步 / 每 N 步窗口),调整调用
scalar()的频率即可,无需手动指定坐标 - Web 曲线在每个 epoch 起点画分界虚线(标注
e0/e1/ …),悬浮提示显示epoch=N · step X MetricGroup末尾收尾交给StepsBar的自动记录(见下文),epoch 沿用绑定值
控制台消息
print 风格的控制台输出接口:多参数自动转 str()、以 sep 拼接,签名对齐 print
(支持 sep / end / flush):
melog.log("普通消息", {"k": 1}) # 终端默认色(黑字),无前缀
melog.success("保存完成") # 绿色 ✔
melog.error("加载失败") # 红色 ✘
melog.warn("学习率过大") # 黄色 ⚠
实例存活期间(仅 rank0),官方 print(...) 会被拦截内部改走 log()——普通打印
自动带上图标/配色并同步进 console.log,进程退出收尾后还原原生 print。颜色仅在真实
终端(TTY)启用,重定向 / console.log 始终纯文本。
多 GPU 下日志消息默认仅 rank0 输出(与指标记录一致,避免 N 卡重复刷屏);
调试需要各卡都打印时传 all_ranks=True(非主卡仅上终端,不进 console.log——
镜像只在 rank0 挂载):
melog.log("只有 rank0 打印") # 默认:其余卡静默
melog.log("每个卡都打印自己的 local 值", all_ranks=True) # 调试用
记录图像与音频
除指标曲线外,Web 端 header 可在 曲线 / 图像 / 音频 三个页签间切换。图像与音频用
image / audio 记录,Web 端按名字建卡片、滑杆按 step 回放(图像点击看原图,
音频在线播放);文件自动落盘到 run_dir/media/,元数据随日志持久化,历史日志加载时
媒体一并恢复:
melog.scalar({"loss": loss}) # 坐标自动依附当前 epoch(StepsBar 绑定)
melog.image("train/sample", img) # 路径 / PIL / numpy / torch
melog.image("val/sample", img) # 自动附着最近一次 scalar() 的位置
melog.audio("val/audio", wav, sr=16000) # 路径(wav/mp3/…) / numpy / torch 波形
- 图像 / 音频自动附着到最近一次
scalar()的位置,不推进计数; 坐标(epoch / step)由StepsBar统一管理,接口无坐标参数 caption="..."可为每条图像 / 音频配一段文字(如样本说明、转写文本), 显示在卡片上、随滑杆切换;换行会被保留- 图像:
(H,W)灰度或(H,W,C)(C=1/3/4),浮点自动映射 0-255,统一存为 PNG - 音频:
(N,)单声道或(N, 声道数),浮点按 [-1,1] 裁剪存为 16bit WAV; 传文件路径则按原格式复制 - 数组编码需要
pillow(仅图像):pip install pillow
指标计算(多 GPU 自动同步)
内置 Mean / Sum / Last / Count,按 epoch 组织在 MetricGroup 中使用:
import melog
from melog import Last, Mean, MetricGroup, StepsBar, Sum
melog.init("runs/my-exp")
metrics = MetricGroup({
"loss": Mean(), # 各 batch 等权平均
"acc": Mean(),
"seen": Sum(), # 求和
"lr": Last(), # 最近一次喂入值
})
for epoch in range(epochs):
# metrics=... 传入后:每次 feed 自动把本卡本地值写入日志/面板
# (实时曲线,零通信),epoch 末自动跨 GPU 合并出全局值再记录
# 一次并 reset 清零。训练曲线(loss / recall 等)无需跨卡合并时传
# reduce=False:bar 打开期间的所有记录都跳过 all_reduce,只记
# master 实时值(各 rank 无需在对齐位置调用);epoch 末只 reset
for _ in StepsBar(range(steps), epoch=epoch, metrics=metrics, reduce=False):
metrics.feed(loss=loss, acc=acc, seen=batch_size, lr=lr)
# feed(..., write=False) 只累积内存(如验证集不想逐 batch 写曲线);
# epoch 末仍自动跨 GPU 合并记录 + reset,无需手动 scalar(默认
# reduce=True,对验证集测试集结果多卡合并)
-
Mean默认按 StepsBar 自动识别的批次样本数精确平均(feed 无需传 batch_size;各 batch 等样本数时即等权);多 GPU 下合并为全局样本平均, 而非"各卡平均值的平均" -
批次样本数自动识别(tensor / numpy 的 shape[0]、字典、列表 / 元组递归); 识别失败(如迭代 range)回退等权平均并警告一次。需手动指定时传元组
(值, 观测数):metrics.feed(loss=(loss, token_num)),显式值优先 -
melog.scalar(metrics)随时可落盘当前累计值;必须算完一个 epoch 才有意义的指标, 在 epoch 末统一记录一次即可(交给 StepsBar 自动执行,或手动调用) -
跨 GPU 合并是集合操作:所有 rank 必须以相同顺序执行,返回值各 rank 一致;单进程自动直通
-
实时 + 精确一步到位:
StepsBar(loader, epoch=e, metrics=metrics)—— 每次 feed 自动把本卡本地值写入日志/面板(零通信,仅 rank0 落盘),bar 同步实时显示; 迭代自然结束时自动 gather 所有 rank 合并出全局值再记录一次(提前 break / 抛异常不触发, 以免各 rank 在 all_gather 处互相等待;所有 rank 都会执行,落盘仅 rank0)。feed(..., write=False)关闭逐 batch 实时写入(如验证集场景),epoch 末 仍自动合并记录,无需手动 scalar -
bar 跑完自动打印最终结果:
StepsBar(metrics=...)迭代自然结束时把 本 bar 监控的 metrics 最终结果打印到控制台(bar 行上方一行)——reduce=True打印跨卡合并后的结果(与落盘值一致),reduce=False打印本卡本地累计值(重置前);未观测到的指标(NaN)与非数值结果不 打印,仅 rank0 输出;print_result=False关闭 -
on_end=...:epoch 末自动记录完成后触发的回调,参数为跨 GPU 合并后的 指标字典(与落盘值一致,未观测到的指标为 NaN)。典型用途如按验证指标 保存 checkpoint。需配合metrics使用且reduce=True(reduce=False 时 epoch 末不做合并记录、本回调不触发,同时传入会被拒绝);所有 rank 都会 执行(合并是集合操作),各卡收到的值一致,仅想主卡执行时在回调内自行 判断 rank;提前 break / 抛异常不触发:for _ in StepsBar(val_loader, epoch=epoch, metrics=val_metrics, on_end=lambda m: save_if_best(m["acc"])): val_metrics.feed(args=(logits, labels), loss=loss, write=False)
tab 面板分区(train / val / test)与 step 隔离
StepsBar 传 tab(或 melog.scalar(..., tab=...))把记录分到面板独立
分区——记录名自动加前缀(train/loss),Web 面板按显式声明的分区把
卡片分到独立区块、垂直排列(不靠命名猜测);分区内仍按指标名分卡,
recall/class_0 式逐类命名照常合并为一张多系列卡片。分区随日志持久化,
历史日志重新加载时一并恢复。
各分区的 step 独立计数、互不影响:train 的 40 步用 train 分区的 x 0..39,val 的记录从 val 分区的 x 0 起(写 val 不推进 train 的步数), 面板左侧切换分区后各自独立展示:
def make_metrics():
return MetricGroup({
"loss": Mean(),
"lr": Last(),
**{f"recall/class_{c}": Recall(num_classes=K, class_index=c) for c in range(K)},
})
train_metrics = make_metrics() # → train/loss, train/lr, train/recall/class_0...
val_metrics = make_metrics() # 同一套定义挂到不同分区
for epoch in range(EPOCHS):
# 训练分区:只看 master 实时指标,reduce=False 跳过跨卡合并
for _ in StepsBar(train_loader, epoch=epoch, tab="train",
metrics=train_metrics, reduce=False):
train_metrics.feed(...)
# 验证分区:结果需对验证集跨卡合并(默认 reduce=True)
for _ in StepsBar(val_loader, epoch=epoch, tab="val", metrics=val_metrics):
val_metrics.feed(write=False) # epoch 末自动合并记录 + reset
- 分区是 StepsBar 的
tab=...显式属性而非命名约定:不传tab时记录进 默认序列(loss、recall/class_0等原有命名分组不受影响) - StepsBar 声明了
tab时,bar 内不带tab的melog.scalar(...)记录 也归入该分区 - 完整示例见
examples/tab_demo.py
feed 如何分发观测
metrics.feed(args=..., **scalars) 把一个 batch 的观测一次喂入,两类指标
分开传、各取所需。以
metrics = MetricGroup({"loss": Mean(), "macc": MaskedAcc()})
metrics.feed(args={"logits": logits, "labels": labels, "mask": mask},
loss=(loss, batch_size))
为例,一次 feed 内部的流转:
args=:观测型指标(如"macc"与所有内置分类指标)的观测, 单独成组——字典按键名对应各指标compute/prepare的形参 (推荐,形参多时更可读),自动分发给形参名匹配的指标,多余的键忽略; 元组按位置喂给未被注册名喂入的指标。 缺少必需形参才抛KeyError。**scalars:按注册名喂入的指标(Mean/Sum/Last/Count,如"loss"): 按注册名找同名键——取出loss=(loss, batch_size);是元组就展开为feed(loss, batch_size)加权累积,普通数值则等权。本 batch 没有同名键就跳过 (不累积也不报错)。
一句话:观测型指标的观测放 args,标量指标按注册名"点名取值"。两类规则
互不干扰,所以同一个 feed 调用可以同时喂两类指标;无主的多余观测两边都不收。
单独使用某个指标时规则一致:标量指标位置喂入 Mean().feed(value, count);
观测型指标具名或位置均可 MaskedAcc().feed(logits=..., labels=..., mask=...),
框架同样按 compute / prepare 形参名组装。
分类指标
内置 Accuracy / Precision / Recall / F1 / ConfusionMatrix,接口与基础指标一致,
feed(logits, labels) 直接接收模型输出与标签:
from melog import Accuracy, F1, MetricGroup, Mean, Precision
metrics = MetricGroup({
"loss": Mean(),
"acc": Accuracy(), # 二分类:一维得分按阈值 0.5 判定
"acc5": Accuracy(topk=5), # top-5 准确率(多分类)
"f1": F1(num_classes=10), # 多分类:二维 (N, K) logits 按行 argmax
})
# 验证集:write=False 不逐 batch 写曲线,epoch 末自动跨 GPU 合并记录
for logits, labels in StepsBar(val_loader, epoch=epoch, metrics=val_metrics):
# feed:观测型指标的观测放 args(元组按位置 / 字典按键名),
# 标量指标按注册名喂入(loss 自动按批次样本数平均)
val_metrics.feed(args=(logits, labels), loss=loss, write=False)
# 无需手动 scalar:StepsBar 结束时自动合并记录并 reset
Accuracy(topk=k):真实类别在前 k 个预测中即算正确Precision / Recall / F1的average:None(二分类=正类,多分类=macro)/"macro"/"micro"/"weighted"ConfusionMatrix的compute()返回矩阵(行=真实、列=预测),适合直接读取而非画曲线- 预测规则由
preds_from_logits实现,可传predictor=替换(如多标签、分割等自定义转换)
自定义指标
统一继承 Metric,按指标何时出值选择实现方式:
实时指标——只实现 compute():每次喂入立即用本批观测算出指标值,
形参名和个数完全由你定义,框架按形参名自动从 feed() 的观测中取值回调;
各 batch 结果按各自实际的样本数加权平均、跨 GPU 合并,全部由框架完成:
from melog import Metric
class MaskedAcc(Metric):
"""需要几个参数就声明几个,logits/labels 仅为示例。"""
def compute(self, logits, labels, mask):
hits = ((logits.argmax(-1) == labels) & mask).sum()
n = mask.sum()
return (hits / n, n) # 返回 (值, 观测数):按样本数平均出全局结果
# 训练循环里:位置或具名喂入均可,多余观测自动忽略
metric.feed(logits, labels, mask)
metric.feed(logits=logits, labels=labels, mask=mask)
compute返回(值, 观测数)元组:各 batch 按观测数(如样本数)平均(样本数不同时务必带上); 只返回 float 时各 batch 等权平均- 组合使用时交给
MetricGroup.feed(...)统一分发:
metrics = MetricGroup({"loss": Mean(), "macc": MaskedAcc()})
# 每个 batch:feed 把观测累积进各指标的内存状态并自动记录本卡实时值
# (观测型指标观测放 args,标量指标按注册名,Mean 自动按批次样本数平均)
for logits, labels, mask in StepsBar(loader, epoch=epoch, metrics=metrics):
metrics.feed(args={"logits": logits, "labels": labels, "mask": mask},
loss=loss)
# epoch 末无需任何手动调用:StepsBar 结束时自动跨 GPU 合并记录 + reset
不用 StepsBar 包裹时(如独立验证脚本)才需要手动落盘:
melog.scalar(metrics) 跨 GPU 合并记录,metrics.reset() 清零开启下一轮。
epoch 级指标——加实现 prepare():全局结果无法由各 batch 值按样本数加权平均还原时
(如 macro F1、AUC),每次喂入先用 prepare()"备料"——接收同样的观测,
返回本批次贡献的增量(数值 / 字典 / 列表),框架自动累积并跨 GPU 合并
(数值求和、字典按键合并、列表拼接);epoch 末把合并后的总量交给
compute() 算出全局结果:
from melog import Metric
class F1(Metric):
"""epoch 末才能计算的指标:累积混淆计数,末尾统一算。"""
def prepare(self, tp, fp, fn): # 每个 batch:本批次贡献的计数
return {"tp": tp, "fp": fp, "fn": fn}
def compute(self, tp, fp, fn): # epoch 末:由总量算出全局值
return 2 * tp / (2 * tp + fp + fn) if tp + fp + fn else float("nan")
# 使用(通常放进 MetricGroup / StepsBar 自动记录):
f1 = F1()
f1.feed(tp=2, fp=1, fn=0) # 位置或具名喂入均可
f1.result() # 跨 GPU 合并并计算(单进程直通)
多 GPU
代码无需修改:按你的分布式训练方式(torch.distributed 初始化完成),
每个 rank 进程各自跑同一份 melog 代码,melog.init 后库自动感知
分布式环境(未装 torch 或单进程运行则一切退化为本地直通,行为完全一致)。
自定义 Metric 时多卡对你透明,不需要写任何分布式代码。compute()
拿到的永远是框架合并好的全量,合并规则按指标类型:
实时指标(只实现 compute)——各卡的本批值按各自实际的样本数加权平均:
全局值 = Σ(各卡每批的 值 × 该批样本数) / Σ(各卡每批的 样本数)
即框架把每卡的 (值, 观测数) 对累加后相除——等价于把所有卡的样本
放到一起算,而非"各卡平均值的平均"。
epoch 级指标(prepare + compute)——各卡 prepare() 返回的
增量先在本地逐 batch 累积,epoch 末把各卡的本地增量跨卡合并,规则只有
三条(可递归组合):
| 增量形态 | 合并规则 | 例 |
|---|---|---|
| 数值 | 求和 | {"tp": 3} + {"tp": 2} → tp=5 |
| 字典 | 按键递归合并(值按同规则) | 两卡各自 {"(1,1)": 2, "(1,0)": 1} 与 {"(1,1)": 1} → {"(1,1)": 3, "(1,0)": 1} |
| 列表 / 元组 | 拼接 | 两卡各 64 个 (得分, 标签) 对 → 128 个 |
三条规则都与合并次序无关(求和与拼接可结合),所以"逐 batch 累积"和
"跨卡合并"用的是同一套规则;epoch 末合并后的总量按 compute() 的
形参名传入(字符串键字典展开为关键字参数,其余作为单个位置参数)。
由此带来的性质:
- 不要求各卡数据同构:各卡观测到的类别、样本数可以不同,计数按 键自动对齐;逐样本对自动拼接,结果与单卡全量计算一致。
- 唯一的纪律:所有 rank 以相同顺序喂入(正常训练代码天然满足)。
记录交给
StepsBar/MetricGroup自动完成即自动满足;手动melog.scalar(...)时所有 rank 执行同一位置即可。 - 验证正确性最简单的办法:单进程跑一遍的
result()应等于多卡 全局值(合并规则保证)。
API
melog.init(...)
| 参数 | 默认 | 说明 |
|---|---|---|
log_dir |
./melog_runs |
日志保存路径(即 run 目录,日志直接落在其中);重跑同一目录即断点续训 |
web_port |
随机空闲端口 | Web 监听端口(web_host 默认 127.0.0.1,地址启动时自动打印,也可读 melog.current().web_url) |
enable_web |
True |
启动 Web 服务(仅 rank0) |
enable_progress |
True |
启用控制台进度条 |
reduce_op |
"mean" |
多 GPU 合并方式 |
flush_every |
1 |
每 N 次 scalar 落盘一次 |
project |
log_dir 末级目录名 |
覆盖项目名 |
主要方法
scalar(metrics, advance=0)— 记录一批指标(dict 或 MetricGroup,后者跨 GPU 合并由内部完成);坐标由StepsBar自动管理(epoch 绑定 + 内部计步),调用频率即记录粒度(见上文)MetricGroup(metrics, category=None)— 具名指标集合(from melog import MetricGroup),feed(...)统一分发观测、跨 GPU 合并;category=...传大类别(train / val / test),Web 面板按类别垂直分区(见上文)image(name, data, caption=None)/audio(name, data, sr=22050, ...)— 记录图像 / 音频,自动附着最近一次记录位置,Web 端页签展示(见上文)StepsBar(iterable, epoch=None, tab=None, metrics=None, reduce=True, on_end=None)— tqdm 风格训练进度条(from melog import StepsBar,模块级melog.stepsbar(...)等价),epoch 循环必须用它包裹:包裹可迭代对象即自动推进,scalar()指标实时显示在条上;epoch=...绑定当前 (分区, epoch) 并统一管理坐标;tab=...声明面板分区(各分区 step 独立计数);metrics=...传入 MetricGroup 时 bar 实时显示本卡本地值(feed 零通信刷新),迭代自然结束自动 gather 全局值合并记录并重置组内指标(reduce=False只记 master 实时值、不做合并;提前 break / 异常不触发);on_end=...在合并记录后收到合并后的指标字典(见上文)- 允许嵌套(如训练 bar 内嵌验证 bar):内部以栈管理,
current_bar()返回栈顶即当前环境;scalar()的 postfix 与advance自动作用于栈顶,下层 bar 暂停渲染(数据照常累计),栈顶关闭后自动恢复下层渲染;提前 break / 抛异常时 bar 自动出栈(如需立即定稿可close()或用 with),否则等引用释放时兜底 current_bar()— 当前栈顶进度条(无打开的 bar 时None);深层函数需要手动推进 / 读数 / 写 postfix 时取它,免层层传参log / success / error / warn— print 风格控制台消息([MM-DD HH:MM:SS][文件名]前缀 + 图标 + 彩色文字),print被拦截改走log();多 GPU 下默认仅 rank0 输出,all_ranks=True时各卡都输出(见上文)- 收尾无需手动调用:进程退出时经 atexit 自动落盘剩余指标、定稿进度条、停 Web、还原 print
- 全局共享:
melog.init(...)创建活动实例后,模块级melog.scalar(...)等可在任意位置直接调用(见上文)
进度条显示的指标可通过环境变量 MELOG_DISABLE_PROGRESS=1 全局关闭。
CLI 快速查看
安装后可直接用命令行查看历史日志,自动打开浏览器:
melog F:/runs/exp1/metrics-20260903_101010.melog # 指定会话日志(自动合并同目录全部会话)
melog F:/runs/exp1 # 指定 run 目录(合并其全部会话文件)
melog # 缺省在 ./melog_runs 中查找
melog F:/runs/exp1 --port 9000 --no-browser # 自定义端口 / 不开浏览器
开发
pip install -e ".[dev]"
pytest tests -q
Metadata
Release files for melog 1.5.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| melog-1.5.2-py3-none-any.whl | Python 3 | none | any | Details |
Release files / melog-1.5.2-py3-none-any.whl
| Download URL | melog-1.5.2-py3-none-any.whl |
|---|---|
| Size | 455.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d086bd6067424b50d47ea6f0f32f546b8b4c447e2817332d418cb32b346791eb
|
|
BLAKE2b-256 checksum How to use checksums |
437eea8abf51ed6a1d217fb83d7a28ed9b2dd8339c6bea0c29975564ac8abe9d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.20
|
Security for melog 1.5.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.