PaMIR
Public Arrival-ordered Measurement for Inference in Risk
The name doubles as the Pamir mountains — the "Roof of the World"; the datasets themselves span Poland, Taiwan, Brazil, Estonia, the US and beyond.
An open benchmark for credit-default prediction when labels are scarce and arrive late. PaMIR rebuilds 19 public credit-default datasets (1.2 million rows, 9 named countries, default rates 3%–41%) from pinned source snapshots by one leakage-audited recipe and never redistributes them; to our knowledge it is the largest open collection of its kind. Every model is a single function, scored under a label-delayed stream, in which each application is scored on arrival by a model trained only on outcomes that have matured, with AUC reported by label budget, and under a conventional i.i.d. split for comparison with other tabular benchmarks. A leakage-controlled synthetic-data harness tests the remedy most often proposed for scarce labels. Because a model is one function, PaMIR can run as a credit track next to general benchmarks such as TabArena and BeyondArena.
Why another benchmark?
Credit-scoring methods are usually compared on two or three small public tables, and the reference multi-dataset credit studies are not reproducible: Baesens et al. (2003) and Lessmann et al. (2015) use eight datasets each, of which two and four are public. General tabular benchmarks contain credit tasks, but evaluate them on random, temporal or grouped splits. A credit model is trained on outcomes that mature months or years after origination, and starts with no labels at all; a temporal split orders the data by time but can still train on outcomes that matured after its cut-off. PaMIR targets that constraint:
- Applications arrive in a stream, and each is scored once, on arrival.
- Outcomes are revealed only after a maturation delay.
- A model deployed on day one has no labels; its learning curve under the delay is part of the result (the label-budget curve).
PaMIR replays each dataset in a fixed random order — most sources carry no usable origination dates — so the stream has no calendar drift: the protocol measures learning under scarce, delayed labels, not robustness to temporal shift.
Comparison with existing benchmarks
| Benchmark | Datasets | Domain | Evaluation |
|---|---|---|---|
| OpenML-CC18 | 72 | General | i.i.d. cross-validation |
| TabArena | 51 | General | i.i.d. cross-validation |
| MultiTab | 196 | General | i.i.d. splits |
| TableShift | 15 | General | explicit domain shifts |
| TabReD | 8 | Industrial | time-based splits |
| BeyondArena | 142 | General (11 credit-default) | i.i.d., temporal and grouped splits |
| Lessmann et al. (2015) | 8 (4 public) | Credit | i.i.d. splits |
| PaMIR | 19 | Credit default | i.i.d. and label-delayed streaming |
Counts are as reported by each benchmark's paper; the BeyondArena credit-default count is ours, from its dataset table as published in June 2026.
Synthetic augmentation
pamir.synthetic mixes real and synthetic training rows in stated proportions
across several generators at once, fits every generator inside the
cross-validation fold, and reports both the AUC delta and a full fidelity
battery:
from sdv.single_table import GaussianCopulaSynthesizer
from pamir import load_dataset
from pamir.synthetic import CrossValidatedAugmentation, SDVAdapter, SyntheticMixer
mixer = SyntheticMixer(
generators={"copula": SDVAdapter(GaussianCopulaSynthesizer, name="copula")},
synthetic_share=0.5, # 50% synthetic / 50% real
)
X, y, _ = load_dataset("south_german")
result = CrossValidatedAugmentation(mixer, n_splits=5, seed=42).run(X, y)
result.summary() # baseline vs augmented OOF AUC, and the delta
result.leakage_frame() # the probes that say whether to believe that delta
result.fidelity_headline() # the few fidelity numbers worth reading first
SDVAdapter takes any SDV synthesizer and needs only the synthetic extra.
Adapters also wrap a pool of pre-generated rows (PoolAdapter) or any object
with fit and sample methods (CallableAdapter), and several generators mix
by weights. The package also contains adapters for two generators developed
at zypl.ai (ZganAdapter, ZedgeAdapter); those generators are distributed
separately and are not evaluated by PaMIR (see Conflict of interest below).
Generators never see a held-out row: the split happens first, each fold gets a
freshly reset generator, the test split is never augmented, and pre-generated
pools must be registered per fold. The exact-overlap probes that verify this
run even with fidelity=False, and hash rows under the real table's schema, so
an engine that returns a column as floats — or as text — cannot slip a
reproduced row past them. See the synthetic-augmentation guide in docs/synthetic.md.
Installation
PaMIR is not on PyPI yet, so install from a clone:
git clone https://github.com/zypl-ai/pamir
cd pamir
pip install -e ".[data]"
PaMIR ships the catalog, the harmonization recipe and the evaluation code —
not the datasets. Each dataset is fetched from its original source and
harmonized locally on first use (then cached). The [data] extra installs the
fetch dependencies (kagglehub, huggingface-hub, platformdirs). Twelve
datasets are hosted on Kaggle: kagglehub 1.0 downloads these public datasets
without an account (checked with kagglehub 1.0.2), older versions need Kaggle
credentials. The other seven come from UCI, GitHub and Hugging Face and need
nothing; pamir.download_open() fetches exactly those.
Core dependencies: numpy, pandas, scikit-learn, scipy, pyarrow. No GPU required.
Optional extras:
pip install -e ".[dev]" # pytest
pip install -e ".[synthetic]" # sdv, sdmetrics, xgboost — for pamir.synthetic
Once published, pip install pamir will be the one-line path.
A note on import order (macOS/arm64)
sdv imports torch, and torch and xgboost each ship their own OpenMP
runtime. On macOS/arm64, loading torch first makes the next xgboost.train()
die with a bare Segmentation fault: 11 and no traceback:
import sdv, xgboost # xgboost.train() later segfaults
import xgboost, sdv # fine
import pamir.synthetic loads xgboost for you before anything touches sdv, so
importing PaMIR first is enough. If your own script imports sdv directly,
put import xgboost above it.
Quick start
from pamir import load_catalog, load_dataset, evaluate, evaluate_iid, fleet_summary
from pamir import gbdt_fit, gbdt_baseline # the same model, in both forms
catalog = load_catalog() # one row per dataset
print(catalog[["name", "rows", "DR", "geography", "license"]])
X, y, meta = load_dataset("gmsc") # features, binary target, metadata
print(f"{meta['name']}: {meta['n_rows']} rows, {meta['n_defaults']} defaults")
results = evaluate(gbdt_fit) # streaming protocol, reference setting
fleet_summary(results) # headline AUC — and whether it is valid
results_iid = evaluate_iid(gbdt_baseline, n_seeds=5) # conventional i.i.d. split
Writing your own model
The streaming protocol calls a fit_fn(X_train, y_train) -> scorer; the
returned scorer(X_rows) -> scores scores rows as they arrive. The i.i.d.
protocol calls a predict_fn(X_train, y_train, X_test) -> scores. Higher
scores mean a higher probability of default. X arrives with raw dtypes —
most of the datasets carry object or bool columns — so encode them, using
only the training rows:
from pamir.baselines import apply_encoder, fit_encoder
def my_fit(X_train, y_train):
from sklearn.linear_model import LogisticRegression
levels = fit_encoder(X_train) # category levels from the training rows
clf = LogisticRegression(max_iter=1000)
clf.fit(apply_encoder(levels, X_train).fillna(0), y_train)
return lambda X: clf.predict_proba(apply_encoder(levels, X).fillna(0))[:, 1]
def my_predict(X_train, y_train, X_test): # the same model for evaluate_iid
return my_fit(X_train, y_train)(X_test)
A scorer must score each row on its own. The streaming protocol passes it the
rows that arrived since its refit in one batch and checks, on every batch, that
a random subset scored alone gets the same scores; a scorer that, say, encodes
categories against the batch's own levels fails that check, and the run gets no
fleet mean. A three-argument predict_fn is accepted by evaluate too
(pamir.from_predict_fn wraps it, one fit per scoring batch).
Always check the summary
If the model raises, the affected rows go unscored. evaluate records every
failure rather than discarding it, and fleet_summary withholds the headline
mean unless the run is valid — because an average over the datasets a model
survived is not comparable with one over all of them:
results = evaluate(my_fit)
summary = fleet_summary(results)
summary["complete"] # every dataset scored
summary["contract_ok"] # every table matched its data contract
summary["row_independent"] # every scorer scored rows independently
summary["auc_mean"] # None unless all three hold
summary["failed_datasets"] # which ones, and results["errors"] says why
summary["auc_by_budget"] # AUC by the number of labels behind each score
While developing, pass on_error="raise" to get the traceback instead.
Report your protocol parameters
mode, lag, max_lag_frac, k_refit and max_n change the score, not just
the runtime. Two runs are comparable only when all of them match. The package
defaults — mode="arrival", lag=1000, max_lag_frac=0.2, k_refit=10,
max_n=20000 — are the reference setting; report them alongside any number,
and state any deviation.
The streaming protocol
The protocol simulates a lender deploying a model on an arriving stream of loan applications.
flowchart LR
S["Applications arrive in stream order<br/>a[1] a[2] … a[N]"]
S --> R["<b>Resolved</b><br/>a[1 .. t−lag]<br/>features + matured outcomes"]
S --> P["<b>Arriving</b><br/>a[t+1], a[t+2], …"]
R -->|"fit on matured labels<br/>refit every k defaults"| M(["Scorer"])
M -->|"score on arrival — final"| P
P -.->|"outcome revealed lag positions later"| R
The rules:
- Stream order. Applications arrive in the order they are stored: a fixed random permutation of the source, because file order in the sources is an export artefact, not an origination sequence.
- Label-maturation delay. The outcome of the application at position t
is revealed at position t + lag (
lag=1000, capped at 20% of the stream for short datasets). At any moment the latestlagapplications are unlabelled. - Refit cadence. The model is refitted once
min_defaultsdefaults have resolved, then after every k newly resolved defaults (k_refit=10): the update schedule is driven by information arrival, not wall time. - Score on arrival. Each application is scored once, by the scorer that was current when it arrived; that scorer was trained only on outcomes that had matured by then. Scores are final.
- Metric. ROC AUC over every scored application, plus the AUC by label budget. Applications that arrive before the first refit are not scored.
The 0.3.0 protocol, which re-scored the whole remaining stream at every refit
(including applications that had not yet arrived), remains available as
mode="refresh" to reproduce 0.3.0 numbers; see docs/protocol.md.
Datasets
All 19 datasets (1,237,550 rows, 281,766 defaults, DR 3.2–40.9%):
| id | name | rows | DR | geography | product | license |
|---|---|---|---|---|---|---|
| bankruptcy | Taiwanese Bankruptcy Prediction | 6,819 | 3.2% | Taiwan | Corporate | © authors |
| bondora | Bondora P2P Lending | 266,482 | 40.9% | Estonia, Finland, Spain | P2P consumer | CC0 |
| conorsully | Conor Sully Credit Score | 1,000 | 28.4% | Synthetic / educational | Consumer | CC0 |
| dish | Automobile Loan Default | 121,856 | 8.1% | Unspecified | Vehicle finance | CC0 |
| gastonstat | Gaston Sanchez Credit Scoring | 4,454 | 28.1% | Unknown | Consumer | None |
| gmsc | Give Me Some Credit | 150,000 | 6.7% | USA | Consumer revolving + installment | Unknown |
| laotse | Laotse Credit Risk | 32,581 | 21.8% | Unspecified | Consumer | CC0 |
| lc_clean | Lending Club (cleaned, 2007–2014) | 150,000 | 20.2% | USA | P2P consumer | None |
| lc_my | Lending Club (Malaysian variant) | 100,000 | 22.6% | Unspecified | Consumer | Unknown |
| lc_small | Lending Club (small, 9,578 loans) | 9,578 | 16.0% | USA | Consumer | ODbL |
| lt_vehicle | L&T Vehicle Loan Default | 233,154 | 21.7% | India | Vehicle finance | Other |
| pakdd | PAKDD 2010 Credit Data | 50,000 | 26.1% | Brazil | Consumer | None |
| poland_1yr | Polish Companies Bankruptcy (1-year horizon) | 7,027 | 3.9% | Poland | Corporate | CC-BY-4.0 |
| poland_3yr | Polish Companies Bankruptcy (3-year horizon) | 10,503 | 4.7% | Poland | Corporate | CC-BY-4.0 |
| poland_5yr | Polish Companies Bankruptcy (5-year horizon) | 5,910 | 6.9% | Poland | Corporate | CC-BY-4.0 |
| prosper | Prosper Marketplace Loans | 55,084 | 30.9% | USA | P2P consumer | CC0 |
| sba | U.S. SBA Loan Defaults | 2,102 | 32.6% | USA | Small business | CC0 |
| south_german | South German Credit (corrected) | 1,000 | 30.0% | Germany | Consumer | CC-BY-4.0 |
| taiwan | Taiwan Credit Card Default | 30,000 | 22.1% | Taiwan | Credit card | CC0 |
Inclusion criteria: a flat table (or reducible to one by the recipe), binary default target, publicly downloadable, ≥1,000 rows, ≥3% default rate.
Data provenance and harmonization
PaMIR distributes no data. pamir.download(id) fetches the raw file from the
dataset's original source and harmonizes it locally with a reproducible recipe
(pamir.harmonize), so every user reconstructs the identical table. The recipe
per dataset (in the catalog's download / harmonize fields):
- Target binarization — a numeric parse or a dataset-specific rule; stored
as
__target__(0 = non-default, 1 = default). - Rescaling (lc_my): Credit Score inflated 10x on defaulter rows is divided back down.
- Post-outcome leakage removal (semantic cut) — every column the
elicitation marked
day_zero_available = false(not known to the lender at decision time) is dropped, plus dataset-specific extra drops (e.g.sba:Term,RealEstate,daysterm). This replaces the earlierAUC > 0.95rule, which let sets of individually-weak columns leak the outcome together. - Column hygiene — drop id / date / constant / surrogate-key columns.
- Row shuffle — fixed seed; file order is not an origination sequence.
Each rebuilt table is checked against its data contract in
pamir/data/expected.json — feature columns, row and default counts, and
SHA-256 digests of the raw file and of the target vector in stored order. A
table that does not match is refused (pamir.ContractError) rather than cached,
and a cached table is re-checked on every load; strict=False keeps a
mismatching table, flagged contract_ok = False, and such a run gets no fleet
mean. The catalog pins the source snapshot (a Kaggle version, a Hugging Face
revision). pamir.dataset_info(id) returns the full recipe, with source
(where the data originate) and fetched_from (what the downloader retrieves).
API reference
pamir.load_catalog() → DataFrame
One row per dataset, indexed by id. Columns include name, rows,
features, defaults, DR, source, source_url, fetched_from, license,
geography, product, target_definition.
pamir.list_datasets() → list[str]
The sorted list of dataset ids.
pamir.dataset_info(dataset_id) → dict
The full metadata dictionary for one dataset. Raises KeyError if the id is
not in the catalog.
pamir.download(dataset_id, force=False, quiet=False, strict=True) → Path
Fetch one dataset from its pinned source, harmonize it, check it against its
data contract and cache it as parquet (with a provenance record). Called
automatically by load_dataset on first use. Cache location: platformdirs
user cache, or $PAMIR_CACHE. With strict=True a mismatching table raises
pamir.ContractError and nothing is cached.
pamir.load_dataset(dataset_id, max_rows=None, auto_download=True, strict=True) → (X, y, meta)
Load a single dataset from the local cache (downloading it first if needed;
set auto_download=False to require an explicit download). The cached table
is re-checked against its contract on every load.
- X: DataFrame of features exactly as stored —
int64,float64,boolandobjectall occur, and most of the datasets carry at least one non-numeric column. Nothing is encoded or imputed for you. - y: numpy array of int (0 = non-default, 1 = default).
- meta: dataset metadata plus
n_rows,n_defaults,n_features,contract_ok,contract_issuesandsnapshot_verified.notesis present only on the datasets that required harmonization, so read it with.get().
pamir.logistic_fit(X_train, y_train) → scorer, pamir.gbdt_fit(X_train, y_train, max_iter=150, ...) → scorer
The reference baselines as fit_fns, for the streaming protocol.
pamir.logistic_baseline / pamir.gbdt_baseline are the same models as
predict_fns, for the i.i.d. protocol. Non-numeric columns are encoded
against the training rows' levels (pamir.baselines.fit_encoder /
apply_encoder). logistic_baseline_v03 / gbdt_baseline_v03 are the 0.3.0
baselines, kept to reproduce 0.3.0 numbers.
pamir.encode_features(X_train, X_test) → (train, test)
Ordinal-encode the non-numeric columns of both frames against one shared level set. Legal in the i.i.d. protocol, where the model is handed the rows it must score; in the streaming protocol it would let a row's code depend on rows that arrived after it, which the row-independence check flags.
pamir.evaluate(model, datasets=None, lag=1000, k_refit=10, max_n=20000, mode="arrival", max_lag_frac=0.2, ...) → DataFrame
Run the streaming protocol on all (or a subset of) datasets. model is a
fit_fn (or a predict_fn, wrapped); in mode="refresh" it is a
predict_fn called with the resolved prefix and the remaining stream.
on_error — "warn" (default) records the failure, warns once per dataset
and carries on; "raise" propagates the traceback; "ignore" records it
silently. Failures are counted under every policy.
Returns a DataFrame with columns dataset, mode, n_rows, n_defaults,
DR, lag_effective, k_refit, n_refits, n_refits_ok, n_calls,
n_failures, n_scored, contract_ok, row_independent, auc_final and, in
arrival mode, auc_budget_0 … auc_budget_10000. Pass it to
pamir.fleet_summary — a mean taken by hand over this frame silently excludes
the datasets the model failed on.
pamir.evaluate_one(model, dataset_id, ...) → dict
Same as evaluate for a single dataset, plus refit_points (per-refit
cumulative AUC), errors (the distinct failure messages, up to five) and, in
arrival mode, train_n (the label budget behind each row's score).
pamir.evaluate_iid(predict_fn, datasets=None, train_frac=0.7, max_n=None, n_seeds=5, verbose=True, on_error="warn", strict=True) → DataFrame
Conventional i.i.d. train/test evaluation on the PaMIR fleet, for comparison
with other tabular benchmarks. Each dataset is permuted n_seeds times; the
reported AUC is the mean across seeds. Columns: dataset, n_rows,
n_defaults, DR, train_frac, n_seeds, n_calls, n_failures,
contract_ok, auc_mean, auc_std.
pamir.evaluate_iid_one(predict_fn, dataset_id, ...) → dict
Same as evaluate_iid for a single dataset, plus aucs (per-seed AUCs) and
errors.
pamir.fleet_summary(results) → dict
Summarize a frame from either protocol. Keys:
| key | meaning |
|---|---|
n_datasets, n_scored, coverage |
how much of the fleet was scored |
complete |
True only when every dataset produced an AUC |
contract_ok, row_independent |
every table was the benchmark table; every scorer was row-independent |
auc_mean, gini_mean |
the headline numbers — None unless all of the above hold |
auc_mean_scored_only |
the partial average; diagnostic only, not comparable |
n_failures, failed_datasets |
what went wrong and where |
auc_by_budget |
arrival mode: mean AUC per label-budget group, and how many datasets reach it |
The headline mean is withheld on a partial run by design: an average over the datasets a model survived is chosen by the model's own failures, and can read higher than a working model's average over all of them.
Reference baselines
Two baselines ship with the package. Both run on every dataset without edits, and neither needs an optional dependency:
from pamir import evaluate, evaluate_iid, fleet_summary
from pamir import logistic_fit, gbdt_fit, logistic_baseline, gbdt_baseline
fleet_summary(evaluate(logistic_fit)) # streaming, reference setting
fleet_summary(evaluate(gbdt_fit))
fleet_summary(evaluate_iid(logistic_baseline)) # i.i.d.
fleet_summary(evaluate_iid(gbdt_baseline))
To use XGBoost instead, encode against the training rows first:
def xgboost_fit(X_train, y_train):
import xgboost as xgb
from pamir.baselines import apply_encoder, fit_encoder
levels = fit_encoder(X_train)
bst = xgb.train({"objective": "binary:logistic", "max_depth": 6, "eta": 0.1},
xgb.DMatrix(apply_encoder(levels, X_train), label=y_train), 200)
return lambda X: bst.predict(xgb.DMatrix(apply_encoder(levels, X)))
Cost
In the streaming protocol every refit is one fit plus the scoring of the rows
that arrived since the previous one; at the reference setting the fleet takes
about 4,700 refits (an estimate from the catalog's row counts and default
rates), most of them on the larger, high-default-rate datasets. logistic_fit is the cheap reference point; raise k_refit (and
report it) to trade resolution for time. The refresh mode is more expensive:
each of its refits scores the whole remaining stream.
Documentation
Sphinx docs (local)
Build and view the full documentation:
pip install -e ".[dev]"
pip install sphinx furo sphinx-copybutton sphinx-autodoc-typehints sphinx-llms-txt myst-parser
sphinx-build -b html docs docs/_build/html
open docs/_build/html/index.html
Features: Furo theme with dark/light toggle, copy button on all code blocks, autodoc-generated API reference from docstrings.
LLM-readable docs
The Sphinx build automatically generates two files for LLM consumption:
llms.txt— structured index with links to each documentation page.llms-full.txt— the entire documentation concatenated into a single plain-text file (~800 lines).
Any LLM agent can download yoursite.com/llms.txt to understand the full
PaMIR API and protocol in one request.
HuggingFace dataset card
The huggingface_card/README.md is the dataset card for the HuggingFace Hub
repo. It contains YAML metadata (tags, configs, license) that makes the
benchmark discoverable through HF search filters, plus a standalone description
of the benchmark and its datasets. The card hosts no data.
Running tests
pip install -e ".[dev]"
pytest tests/ -v
The test suite covers: catalog integrity (the dataset list matches the catalog,
all metadata fields, DR/feature consistency, version consistency across release
files), the data contract (exact validation, refusal on download and on load),
harmonized loading, the leakage guards and a positive control for each,
streaming-protocol invariants on synthetic streams (a scorer sees only rows
that arrived after its refit and labels that had matured, every score is final,
the delay cap, the row-independence check, and that the refresh mode
reproduces 0.3.0), the i.i.d. protocol, failure accounting (a partial run yields
no fleet mean), the reference baselines on every dataset, and data quality (no
duplicates, no constant features). The pamir.synthetic augmentation layer
adds its own suite — fold-safe cross-validation, the exact-overlap leakage
probes, and the fidelity battery. Data-dependent tests skip cleanly when a
dataset is not cached (CI holds no Kaggle credentials, so it checks the seven
credential-free datasets); catalog and protocol tests always run.
License
The PaMIR package code is released under the Apache 2.0 license — see the
LICENSE file at the repository root.
PaMIR does not redistribute the datasets — it fetches each one from its
original source at the user's request. Each dataset remains under its own
license and terms, as listed in the catalog
(pamir.dataset_info(id)["license"]); when you use a dataset you are bound by
those terms and are responsible for citing its original authors.
Conflict of interest
PaMIR is developed at zypl.ai, which also develops synthetic-data generators (zGAN, zEDGE). The package contains adapters for those generators so that they can be measured by the same public protocol as any other; the generators themselves are distributed separately, and PaMIR's releases and reports do not evaluate them. Any future evaluation of a maintainer's model or generator will use the published protocol, ship with its code, and be marked as such.
Citation
If you use PaMIR, please cite the software and the original source of every
dataset you use (pamir.dataset_info(id)["citation"]):
@software{pamir2026,
title = {PaMIR: Public Arrival-ordered Measurement for Inference in Risk},
author = {Liashkov, Mikhail and Varshavskiy, Ilyas and Boboeva, Bonu and
Khalilbekov, Shuhrat and Azimi, Azizjon},
year = {2026},
version = {0.4.0},
url = {https://github.com/zypl-ai/pamir},
}
A technical report describing the benchmark is in preparation; this entry will point to it once it is public.
Metadata
Release files for pamir 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pamir-0.4.0.tar.gz | 444.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pamir-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 539.1 kB
Release files / pamir-0.4.0.tar.gz
| Download URL | pamir-0.4.0.tar.gz |
|---|---|
| Size | 444.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45169bfaf50949694664d794268a7035275dd062a6b47e1cde2f913402e0e3c0
|
|
BLAKE2b-256 checksum How to use checksums |
bd102d3a285a247a5d6b4ccdae12cc8fcc354d7105e70c25ec3d7900f01fa660
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / pamir-0.4.0-py3-none-any.whl
| Download URL | pamir-0.4.0-py3-none-any.whl |
|---|---|
| Size | 94.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9451e1124b1b74df16050afb5b80c5e3d3818baf33a55682f6ff304b8e8db4ce
|
|
BLAKE2b-256 checksum How to use checksums |
58161768d1e6af284384db90461c90287789cafb6b83e07f1915a47ccecc36c4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency log