Enforce structural conventions in Python codebases
Project description
konpy
Enforce consistent code, for agents and humans.
konpy is a CLI linter that checks whether files and directories in your Python codebase match declared structural conventions — project layout, required files, required module-level definitions/exports/imports, __init__.py re-export purity, docstring and annotation coverage, naming patterns, dead code — all declared in a single konpy.json. (Distributed on PyPI as konpy; the import package and CLI are konpy.)
Inspired by Vercel's konsistent — reimplemented from scratch for Python.
konpy is deliberately not a style linter or type checker. Formatting belongs to ruff, types to mypy. konpy owns the layer above: "every src/{name}_service.py exports a ${name.toPascalCase()}Service class, has a paired tests/test_{name}.py, documents its public API, and never imports from the infrastructure layer."
Install & run
uv tool install --from /path/to/konpy konpy # or: uv add --dev konpy
konpy # = konpy check, reads ./konpy.json
konpy validate # schema-check the config without scanning
konpy check --config-path other.json --max-diagnostics 300
Quickstart
Create konpy.json at the repo root:
{
"$schema": "./konpy.schema.json",
"version": "v1",
"conventions": [
{
"name": "services-have-shape",
"description": "Every service module exports its class and has a test.",
"paths": "src/{name}_service.py",
"must": {
"exportClasses": ["${name.toPascalCase()}Service"],
"havePairedFile": "tests/test_${name}_service.py",
"haveDocstrings": { "publicOnly": true },
"annotateFunctions": { "publicOnly": true }
},
"mustNot": {
"matchContent": ["\\bFIXME\\b"],
"importFrom": "src.infrastructure"
}
}
]
}
paths globs capture placeholders ({name}) that predicates consume via ${name} templates with case transforms (toPascalCase, toSnakeCase, toConstantCase, …). Everything under must must hold; anything under mustNot must not. Full vocabulary: docs/reference/predicates.md; path grammar: docs/reference/path-patterns.md.
Notable predicates: haveFiles, haveType, export*/declare* families, import/importFrom/importFromParents, areBarrelFiles, useDeclarationOrder, and the coverage/content set: matchContent (regex on file content — the escape hatch), havePairedFile (repo-root-relative), haveDocstrings, annotateFunctions, restrictAnnotations. Dead-code detection is configured separately via the top-level unusedCode key — next section.
Unused-code detection
unusedCode is a classifier, not a flagger — the design goal is zero noise. Instead of reporting every unreferenced name the way vulture or basedpyright's reportUnusedFunction do, it classifies every definition (module-level functions, classes, and constants, plus one level of class-body methods and attributes) and reports only the two actionable classes, staying silent on the systematic false-positive classes that framework code produces:
{
"version": "v1",
"conventions": [],
"unusedCode": {}
}
- Reported:
dead— no reference anywhere (code, tests, entrypoint files, string literals) — andtest-only— referenced only undertestGlobs, i.e. code kept alive purely by its own tests, a delete-with-its-tests candidate no comparable tool surfaces. - Silent: decorator-registered handlers (
@app.*,@field_validator,@pytest.fixture, …), lifecycle hooks and dunders, model fields onBaseModel/TypedDict/Enum/@dataclassclasses, and symbols named in entrypoint files (DockerfileCMD,pyproject.toml, serverless templates). A bare{}already understands pydantic, FastAPI/Flask/Django, pytest, celery, and click/typer via shipped presets.
References are resolved repo-wide, including identifier tokens inside string literals — "src.lambda_function.handler" in a Dockerfile keeps handler alive. Matching is by bare name and deliberately under-reports before it ever false-positives (see limitations).
Findings arrive as warning-severity diagnostics under the [unused-code] label (predicate unusedCode.dead / unusedCode.testOnly in --format json) — gate them in CI with --error-on-warnings, and suppress an approved exception with # konpy: ignore[unused-code] (the consent policy applies). The config keys (include, testGlobs, entrypointFiles, registryDecorators, hookNames, modelBases) extend the presets; allow silences specific names by decision. Because classification needs the whole reference graph, unusedCode always scans the entire project — --files/--changed scoping never narrows or partially runs it. Full taxonomy, presets, and config keys: docs/reference/unused-code.md.
Duplication detection
Two cross-file predicates catch copy-paste before it calcifies. restrictRepeatedLiterals flags a string literal repeated across a convention's matched files (default: the 3rd occurrence violates, minLength: 8; docstrings, annotation positions, __all__/dunder assignments, f-string fragments, and __name__ comparisons are exempt at collection time). restrictDuplicateFunctions flags structurally identical function bodies — hashed after alpha-renaming parameters and locals and stripping docstrings, annotations, and decorators — so renaming variables doesn't hide a clone, while calling different helpers or using different constants keeps functions distinct (default: bodies of 4+ statements).
{
"version": "v1",
"conventions": [
{ "name": "no-repeated-literals", "paths": "src/**/*.py", "must": { "restrictRepeatedLiterals": true } },
{ "name": "no-duplicate-functions", "paths": "src/**/*.py", "must": { "restrictDuplicateFunctions": true } }
]
}
Repeated literals report every occurrence; duplicate functions report each non-canonical member pointing at the first definition — all with expected/found/fix_hint intent metadata. Both predicates always evaluate the convention's full matched set, even under --files. Off the shelf: packs/no-duplication.json. Reference: docs/reference/predicates.md.
Reusable conventions & the best-practices pack
Rules can be packaged once and consumed everywhere. This repo ships a starter pack at packs/python-best-practices.json:
{
"version": "v1",
"conventionSources": { "bp": "./packs/python-best-practices.json" },
"conventions": [
"bp/init-files-are-barrels",
"bp/absolute-imports-only",
"bp/docstrings-on-public-api",
"bp/annotated-public-functions",
{ "use": "bp/paired-test-files", "paths": ["src/{name}.py", "!src/__init__.py"] },
{ "use": "bp/class-name-matches-filename", "paths": "src/{name}_service.py" }
]
}
String form uses the pack rule's own paths; use form supplies (or overrides) paths, placeholders, and severity. Authoring guide: docs/guides/authoring-reusable-conventions.md. Copy-paste templates for project-specific rules (layered import bans, DDD layouts, test-suite layout): docs/guides/templates.md.
More packs: typed records, duplication, hexagonal architecture, and src layout
Additional off-the-shelf packs live alongside the best-practices one:
packs/typed-records.json— annotation hygiene for identity-less anonymous record mappings such asdict[str, Any]; encourages pydantic models,TypedDict, or dataclasses.packs/no-duplication.json— cross-file duplication limits at warning severity: no string literal repeated 3+ times across the scope, no structurally identical function bodies (see Duplication detection).packs/hexagonal-architecture.json— ports-and-adapters layering: domain modules stay free of adapter/infrastructure imports, ports areProtocol/ABCboundaries, adapters export an*Adapter-suffixed class, and each use case has a paired test. Assumessrc/domain/,src/ports/,src/adapters/,src/use_cases/.packs/src-layout.json—src/layout hygiene: the project root hassrc/+pyproject.toml, every top-levelsrc/package has an__init__.py, and both flat and one-level-nested modules mirror intotests/.
Consume either one the same way, via conventionSources:
{
"version": "v1",
"conventionSources": {
"hex": "./packs/hexagonal-architecture.json",
"layout": "./packs/src-layout.json",
"typed": "./packs/typed-records.json",
"dup": "./packs/no-duplication.json"
},
"conventions": [
"hex/domain-does-not-import-adapters-or-infrastructure",
"hex/ports-are-protocols-or-abcs",
"hex/adapters-export-adapter-suffix",
"hex/use-cases-paired-with-tests",
"layout/project-root-uses-src-layout",
"layout/top-level-src-packages-have-init",
"layout/top-level-modules-mirror-into-tests",
"layout/nested-modules-mirror-into-tests",
"typed/no-anonymous-record-annotations",
"dup/no-repeated-string-literals",
"dup/no-duplicate-functions"
]
}
Full per-convention reference, including the layout assumptions each pack makes: docs/reference/packs.md.
Distributing packs on PyPI
A convention source can be a bare Python distribution name. Ship a konpy.json (reusable-package format) as package data, publish, and consumers write:
{ "conventionSources": { "acme": "acme-conventions" } }
after uv add --dev acme-conventions. konpy resolves the installed distribution via importlib.metadata — no network at lint time.
Config inheritance (org base → team → project)
{
"version": "v1",
"extends": ["acme-base-config", "./team-overlay.json"],
"disable": ["legacy-rule"],
"conventions": [
{ "name": "rule-also-in-base", "paths": "packages/{name}", "must": { "haveFiles": ["README.md", "pyproject.toml"] } }
]
}
Parents load left-to-right (local paths or installed package names), then the child overlays: conventions concatenate, a same-name convention replaces the inherited one in place, disable removes inherited rules by name, everything else deep-merges. Cycles are detected and rejected. Details: docs/reference/configuration.md.
Custom rules
Three tiers, cheapest first:
-
matchContent— most "custom rules" are a regex away, no code required:{ "name": "no-naive-utcnow", "paths": "src/**/*.py", "mustNot": { "matchContent": ["\\butcnow\\(\\)"] } }
-
A reusable pack — bundle rules built from existing predicates into a
konpy.jsonpackage (local file or PyPI) so every repo consumes the same definitions. -
Plugin predicates — real custom logic as Python code, loaded from entry points. In your plugin package:
[project.entry-points."konpy.predicates"] requireMarker = "acme_konpy.rules:require_marker"
from konpy.plugin import PredicatePlugin, create_diagnostic def check(*, expected, context, structure, convention_name, severity): if expected not in context.file_system.read_file(context.path): return [create_diagnostic( file_path=context.path, predicate_name="requireMarker", message=f'Missing marker "{expected}"', convention_name=convention_name, severity=severity)] return [] require_marker = PredicatePlugin( key="requireMarker", value_model=str, handler=check, forbidden_message_template='Forbidden marker "{value}"')
Consumers must opt in explicitly — konpy never executes code the config didn't name:
{ "version": "v1", "plugins": ["acme-konpy"], "conventions": [{ "name": "markers", "paths": "src/*.py", "must": { "requireMarker": "PLUGIN_OK" } }] }
Plugin keys work under
mustNottoo, get strict value validation from the plugin's own pydantic model, and collide loudly with builtins. Full contract (AST access viauses_ast, item-level mustNot, placeholder validation): docs/reference/plugins.md.
Using konpy with Claude Code (the agent loop)
konpy was built for exactly one workflow: encode your conventions once in konpy.json, then wire them into every stage of a coding agent's loop — before it writes, after each edit, and in CI — so the agent hears one consistent voice everywhere. The pieces below each have their own section; this is the order they compose in:
- Bootstrap the rules — don't hand-author them. Mine an existing codebase with
konpy infer, translate a prose style guide or skill withextract-rules, or start greenfield from the shipped packs. All three emit reviewable proposals, never live config. - Prevention: put the rules in the agent's context.
konpy explain >> CLAUDE.mdrenders the resolved config as agent guidance — the agent writes conformant code on the first pass instead of getting caught afterwards. Re-run it when the config changes. - Per-edit feedback: a
PostToolUsehook. Runkonpy check --files <edited-file>after everyEdit/Write; on violation the hook exits2and the JSON diagnostics land in front of the agent, which fixes them in the same session — withexpected/found/fixHintso the fix needn't be re-derived from a message string. Recipe: docs/guides/claude-code-hook.md. For judgment calls no structural predicate can express, layer the agentickonpy hookon top (this repo does, via its own.claude/settings.json). - Keep exceptions honest. Suppressions require a named rule and a human decision — the consent policy is restated in every
explainrender, so the agent knows it. - Gate and measure.
konpy check --error-on-warningsin CI, and the eval harness to snapshot violation metrics before/after an agent session and fail on regression.
Division of labor: ruff owns universal style and correctness; konpy owns your architecture — layout, naming-to-export contracts, import boundaries, paired tests, dead code. They complement, not compete.
What an agent sees
When the PostToolUse hook runs konpy check --files <edited-file> --format json, this is the payload that lands in front of the agent. Given a freshly written src/service.py that skips docstrings and annotations and leaves a FIXME behind — checked against a strict pack of docstrings-on-public-api, annotated-public-functions, and a no-fixme matchContent rule — the agent receives:
{
"diagnostics": [
{
"severity": "error",
"conventionName": "docstrings-on-public-api",
"filePath": "src/service.py",
"predicateName": "haveDocstrings",
"message": "Function \"compute_total\" must have a docstring",
"line": 1,
"description": "Public functions must have docstrings.",
"hint": "Add a one-line docstring describing what the function does.",
"expected": "docstring on function \"compute_total\"",
"fixHint": "Add a docstring to function \"compute_total\"."
},
{
"severity": "error",
"conventionName": "annotated-public-functions",
"filePath": "src/service.py",
"predicateName": "annotateFunctions",
"message": "Function \"compute_total\" must have a return type annotation",
"line": 1,
"expected": "return type annotation",
"fixHint": "Add a return type annotation to function \"compute_total\", e.g. `-> <Type>:`."
},
{
"severity": "error",
"conventionName": "no-fixme",
"filePath": "src/service.py",
"predicateName": "mustNot.matchContent",
"message": "Forbidden content matching regex \"\\bFIXME\\b\"",
"line": 2,
"expected": "\\bFIXME\\b",
"found": "FIXME",
"fixHint": "Remove or rewrite the content in src/service.py that matches the pattern `\\bFIXME\\b`."
}
],
"suppressed": [],
"summary": { "filesChecked": 1, "errors": 6, "warnings": 0, "suppressed": 0, "durationMs": 0.86 },
"truncation": { "shown": 6, "omitted": 0 }
}
Every diagnostic carries not just a message but the machine-actionable expected/found/fixHint and convention-level description/hint from Diagnostic intent and fix direction — so the agent applies the fix directly instead of re-deriving it from prose. (Three of the six errors are shown here for brevity; summary.errors is always the full pre-truncation total.) The same run in the default human-readable --format — what you see at the terminal — renders as:
src/service.py
1 error Function "compute_total" must have a docstring [docstrings-on-public-api]
-> description: Public functions must have docstrings. | hint: Add a one-line docstring describing what the function does. | expected: docstring on function "compute_total" | fix: Add a docstring to function "compute_total".
1 error Function "compute_total" must have a return type annotation [annotated-public-functions]
-> expected: return type annotation | fix: Add a return type annotation to function "compute_total", e.g. `-> <Type>:`.
2 error Forbidden content matching regex "\bFIXME\b" [no-fixme]
-> found: FIXME | fix: Remove or rewrite the content in src/service.py that matches the pattern `\bFIXME\b`.
Checked 1 file in 1ms. Found 6 errors.
Extracting rules from skills & style guides
Turn prose best practices — a Claude Code skill's SKILL.md, a team style guide, any markdown — into a reviewable pack:
konpy extract-rules .agents/skills/python-project-structure/SKILL.md
konpy extract-rules style-guide.md -o packs/team-style.json --agent codex --report unmapped.md
It shells out to a local agent CLI (claude -p or codex exec; --agent auto is the default and prefers claude), pins the agent's model via --model (default: sonnet, forwarded as the agent CLI's own --model flag — pass an explicit value for codex), embeds the predicate vocabulary and pack format in the prompt, and validates the result against the pack schema before writing anything. Rules that aren't structurally expressible are never silently dropped — they land in an unmapped-rules report with reasons (that's your ruff/mypy/plugin backlog). The output is a proposal for human review; extract-rules never edits konpy.json. Guide: docs/guides/extracting-rules.md.
Explaining rules to an agent
konpy explain renders your fully resolved konpy.json (after extends/disable/conventionSources/plugins) as concise Markdown or plain-text guidance — one bullet per convention with its name, paths, description, hint, and severity — so you can paste it into CLAUDE.md and have a code-writing agent follow the rules before writing code, not just get caught by check afterwards:
konpy explain > CLAUDE.md
konpy explain --format text
It is read-only: no filesystem scan, no diagnostics, no --fix. Every render ends with a standing reminder of the suppression consent policy — agents must never add a # konpy: ignore[...] comment without explicit human approval.
Mining a codebase for conventions
konpy infer scans an existing codebase for statistical regularities — "94% of modules under adapters/ export *Adapter; here are the 3 violators" — and proposes a reviewable ReusableConventionsPackageV1-shaped pack (the same output contract as extract-rules: {"conventionSpecVersion": "v1", "conventions": [...]}, never a konpy.json-shaped document) plus a confidence/violators report, using eight deterministic heuristics (no agent call). The two duplication heuristics are clean-only ratchets: they propose restrictRepeatedLiterals/restrictDuplicateFunctions only for scopes that already pass at the defaults, and otherwise skip with an existing-violations reason instead of proposing a rule that would immediately fail:
konpy infer > konpy.infer.pack.json
konpy infer --heuristic export-suffix --heuristic paired-test-file -o proposal.json -r report.md
The proposed pack goes to stdout (or --output); the confidence/violators report goes to stderr (or --report). Every proposal is severity: "warning" and carries support/total counts plus a violator list — infer never edits konpy.json and never reads an existing one. Guide: docs/guides/inferring-conventions.md.
Agentic verification hooks
konpy hook wires an agentic verifier into Claude Code's or Codex's PostToolUse hooks: after a matched write/edit, it spawns a read-only verifier agent (claude -p or codex exec) with a natural-language --prompt and turns its pass/fail verdict into the hook exit-code contract (0 pass/skip, 2 fail, 1 infra fail-open), with sentinel-based recursion guards so the verifier can't re-trigger the hook that spawned it.
konpy hook --agent claude --model sonnet --match 'src/**/*.py' --prompt 'Docstrings are not aspirational: verify each function body actually does what its docstring claims.'
The verifier's model is pinned via --model (default: sonnet, forwarded to the agent CLI as its own --model flag; set it explicitly for codex). Exit 2 is reserved exclusively for a real fail verdict: every misconfiguration — missing --prompt/--agent, an invalid --agent value, even an option the installed konpy doesn't recognize — fails open with exit 1 rather than surfacing a CLI usage error to the host agent as if it were review feedback.
This is not the same mechanism as the --files diff-scoped hook recipe below — that one runs konpy check directly (deterministic, no LLM call, verifies konpy.json structural conventions). konpy hook is for checks a structural predicate can't express; use it for the subset of your review that needs judgment, and the deterministic recipe for everything else. This repo dogfoods the agentic hook via its own .claude/settings.json. Guide: docs/guides/hooks.md.
CLI
| Command | Purpose |
|---|---|
konpy / konpy check |
scan and report violations (exit 1 on errors) |
konpy validate |
validate the config only |
konpy explain |
render resolved conventions as agent guidance (see above) |
konpy extract-rules <src> |
agent-assisted rule extraction (see above) |
konpy infer |
mine the codebase for candidate conventions (see above) |
konpy hook |
agentic PostToolUse verification hook (see above) |
konpy version |
print version |
Useful flags: --config-path, --placeholder name:value, --max-diagnostics, --format json, --show-suppressed. Full reference: docs/reference/cli.md.
Diff-scoped checking (--files / --changed)
Scope a run to files an agent just touched — konpy check --files src/service.py or konpy check --changed (tracked changes since HEAD plus untracked files, per git diff/git ls-files). --changed requires a git repository: outside one it prints a single clear message to stderr (not a raw git error dump) and exits 1 — it never silently falls back to a full scan. Scoping is convention-level, not file-level: a convention is selected as soon as any file in scope falls in its matched set, and then it's evaluated over its entire matched set — so a violation on a sibling file the agent didn't touch can still surface. havePairedFile and unusedCode are whole-graph predicates; see Full semantics and edge cases below for exactly how each is handled under scoping.
konpy check --files src/service.py
konpy check --changed
This is what powers the Claude Code PostToolUse hook recipe: run konpy check --files <edited-file> --format json after every Edit/Write so an agent gets scoped, structured feedback on the file it just changed without waiting for a full check. Full semantics and edge cases: docs/reference/cli.md#diff-scoped-checking---files----changed.
Diagnostic intent and fix direction
Diagnostics can carry more than a message: an optional convention-level description/hint (inherited by every diagnostic the convention produces) and predicate-specific expected/found/fix_hint fields, so an agent's next edit doesn't need to be re-derived from a message string.
{
"name": "documented-service",
"description": "Service modules must be paired and documented.",
"hint": "Run the service generator template if you are starting a new service.",
"paths": "src/service.py",
"must": { "havePairedFile": "tests/test_service.py" }
}
{
"predicateName": "havePairedFile",
"message": "Missing paired file: tests/test_service.py",
"description": "Service modules must be paired and documented.",
"expected": "tests/test_service.py",
"fixHint": "Create the paired file at \"tests/test_service.py\"."
}
All five fields are optional and additive: omitted from --format json when absent (never null), and shown as an extra suffix line/cell in default/markdown output only when populated. expected/found/fix_hint are currently populated by exportClasses, exportConstants, havePairedFile, haveDocstrings, annotateFunctions, importFrom, the importFrom*/importTypes* group predicates, and matchContent — other predicates leave them unset rather than guessing. fix_hint is data only; konpy never applies it automatically. Full reference: docs/reference/cli.md#diagnostic-intent-and-fix-direction.
Suppressions
Approved exceptions can be silenced in place, without touching konpy.json:
# konpy: ignore-file[max-module-length] -- splitting tracked in TICKET-123
"""Module docstring."""
def orphaned(): # konpy: ignore[docstrings-on-public-api, unused-code] -- approved legacy hook
...
- Line-level
# konpy: ignore[rule-a, rule-b]on the flagged line (or the line directly above) suppresses those rules' findings anchored to that line. - File-level
# konpy: ignore-file[rule-name]must appear before the first code line and also covers findings with no line anchor (matchContent,havePairedFile,haveType,importFrom). - The bracketed rule list is mandatory — there is no blanket ignore. Names match the
[bracket]label shown in check output. An optional reason follows--.
Suppressions are designed to never be invisible:
- Every summary shows the count:
Found 1 error. Suppressed 3 findings. konpy check --show-suppressedlists each suppressed finding with its reason;--format jsonalways includes the fullsuppressedarray.- Stale or unknown suppressions are themselves reported as warnings (
Unused suppression for "rule-name"), so dead ignores get cleaned up — and they fail CI under--error-on-warnings. - Suppressed errors don't fail the build; the exit code counts only unsuppressed findings.
Policy for AI coding agents: never add a suppression comment without explicit human approval. The correct default is to fix the violation or ask for a decision. When approval is granted, use the narrowest form (line-level over ignore-file) and always include the reason. Full grammar and semantics: docs/reference/suppressions.md.
Agent evaluation
scripts/eval_conventions.py A/B-compares how much structural drift a coding agent introduces or removes under different guidance strategies, using konpy's own diagnostics as the metric. It runs konpy check --format json against a target repo and reduces the result into a stable, diffable metrics summary, so you can snapshot a repo before and after an agent run and diff the two:
uv run python scripts/eval_conventions.py run /path/to/target-repo --label before --output before.json
# ...agent does its work...
uv run python scripts/eval_conventions.py run /path/to/target-repo --label after --output after.json
uv run python scripts/eval_conventions.py compare before.json after.json
The comparison reports files checked, total diagnostics, errors/warnings/suppressed, and per-convention/per-predicate/unusedCode breakdowns as before -> after (delta), plus a PASS/FAIL - errors increased regression check (compare --fail-on-regression turns that into a nonzero CI exit code). It is a repo-dev script — stdlib-only, shells out to the konpy CLI as a subprocess rather than importing the package — meant for pairing with konpy explain guidance and the --files hook to measure their effect on an agent's output. Guide: docs/guides/agent-eval.md.
Development
uv run pytest # full suite
uv run ruff check .
uv run konpy # the repo lints itself
uv run python scripts/generate_schema.py # regenerate konpy.schema.json after schema changes
Docs index: docs/README.md. The TypeScript original lives in tmp/konsistent as a read-only reference; the v1 config grammar is kept compatible (all Python-port additions — extends, disable, plugins, the coverage predicates — are optional keys).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file konpy-0.4.0.tar.gz.
File metadata
- Download URL: konpy-0.4.0.tar.gz
- Upload date:
- Size: 431.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
469cb52a7f18e542cf6358659eb1046bdf044167b695301724710410c1d10593
|
|
| MD5 |
ff38d7c8be0edeecfff03755df0339b7
|
|
| BLAKE2b-256 |
68e11183fdd9b765f5870be0893271013b780ce367d6f4e50fe8f456fe06627b
|
Provenance
The following attestation bundles were made for konpy-0.4.0.tar.gz:
Publisher:
publish.yml on ivorpad/konpy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
konpy-0.4.0.tar.gz -
Subject digest:
469cb52a7f18e542cf6358659eb1046bdf044167b695301724710410c1d10593 - Sigstore transparency entry: 2175286637
- Sigstore integration time:
-
Permalink:
ivorpad/konpy@f56e7f37ddb72442eaba03e235d9d7b0bab4fddd -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/ivorpad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f56e7f37ddb72442eaba03e235d9d7b0bab4fddd -
Trigger Event:
push
-
Statement type:
File details
Details for the file konpy-0.4.0-py3-none-any.whl.
File metadata
- Download URL: konpy-0.4.0-py3-none-any.whl
- Upload date:
- Size: 239.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
532b67546639926201eabbf8966ab1d744a0c68dbbcffe9fb8264f8cf1abadd0
|
|
| MD5 |
9b4361f4828102b81d2065427632ee98
|
|
| BLAKE2b-256 |
a96d20c00f381016793cccf028e69c366193894bc1bb3a5adf9ab913a43a01b7
|
Provenance
The following attestation bundles were made for konpy-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on ivorpad/konpy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
konpy-0.4.0-py3-none-any.whl -
Subject digest:
532b67546639926201eabbf8966ab1d744a0c68dbbcffe9fb8264f8cf1abadd0 - Sigstore transparency entry: 2175286802
- Sigstore integration time:
-
Permalink:
ivorpad/konpy@f56e7f37ddb72442eaba03e235d9d7b0bab4fddd -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/ivorpad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f56e7f37ddb72442eaba03e235d9d7b0bab4fddd -
Trigger Event:
push
-
Statement type: