Skip to main content

artifact-parser

Turn data-tool JSON artifacts into typed, validated Python objects

artifact-parser logo

PyPI version python-lib License: MIT python CI coverage

artifact-parser stars buy me a coffee

A small, pluggable framework for turning the JSON artifacts that data tools spit out into typed, validated Python objects. Point it at a blob, get back a pydantic model — no manual key-spelunking, no guessing which schema version you're holding.

The framework is deliberately source-agnostic. Each plugin owns one family of artifacts and registers itself with a shared registry. The first one ships in the box: a full dbt-core parser (catalog, manifest, run-results, sources).

Install

uv add artifact-parser     # or: pip install artifact-parser

Quick start

The headline entry point sniffs any supported artifact and routes it to the right plugin — you don't have to know what you're holding:

import json
from artifact_parser import parse

artifact = json.loads(open("target/manifest.json").read())
model = parse(artifact)  # -> a ManifestV12 (or whatever version it is)
print(model.metadata.dbt_schema_version)

When you do know the artifact family, the dbt plugin's typed helpers are more precise (and give better editor autocomplete):

from artifact_parser.dbt import parse_manifest, parse_catalog

manifest = parse_manifest(json.loads(open("target/manifest.json").read()))
catalog = parse_catalog(json.loads(open("target/catalog.json").read()))

Hand it something it doesn't recognise and it tells you so, loudly, instead of returning a half-populated object:

from artifact_parser import parse, UnknownArtifactError

try:
    parse({"metadata": {"dbt_schema_version": "made-up/v99.json"}})
except UnknownArtifactError as exc:
    print(exc)  # No registered parser recognises this artifact. Tried: dbt.

Supported dbt artifacts

Artifact Versions Generic parser Version-pinned parsers
catalog v1 parse_catalog parse_catalog_v1
manifest v1–v12 parse_manifest parse_manifest_v1 … _v12
run-results v1–v6 parse_run_results parse_run_results_v1 … _v6
sources v1–v3 parse_sources parse_sources_v1 … _v3

Architecture

src/artifact_parser/
├── core/                 # the framework — no knowledge of any specific tool
│   ├── base.py           #   BaseArtifactModel (shared pydantic root)
│   ├── parser.py         #   ArtifactParser (the plugin contract)
│   ├── registry.py       #   ParserRegistry + the shared `registry` instance
│   └── exceptions.py     #   ArtifactParserError + friends
└── dbt/                  # the first plugin: dbt-core artifacts
    ├── plugin.py         #   DbtArtifactParser (implements ArtifactParser)
    ├── utils.py          #   schema-version sniffing
    ├── resources/        #   committed dbt-core JSON schemas (codegen input)
    └── generated/        #   droppable, rebuilt by `codegen dbt`
        ├── parser.py     #     parse_<artifact>[_vN] public API
        ├── version_map.py#     schema-version URL -> model class
        └── models/       #     typed pydantic models, one module per version

The generated code is walled off in generated/. You can rm -rf that whole directory and rebuild it with codegen dbt (the package still imports while it's gone — the dbt plugin just sits out until you regenerate).

The flow: a plugin answers "is this mine?" (can_parse) and "make it typed" (parse). The registry tries plugins in registration order and returns the first match. dbt registers itself on import, so parse(...) works out of the box.

Adding a new parser

The whole point of the core/ framework is that the second parser is cheap. By hand:

  1. Create src/artifact_parser/<tool>/.
  2. Define your models on BaseArtifactModel.
  3. Implement ArtifactParser (name, can_parse, parse) in plugin.py.
  4. Register it in the package __init__.py: registry.register(MyParser()).
  5. Import your plugin from the top-level artifact_parser/__init__.py.

That's it — parse() now routes matching artifacts to your plugin.

Development

This project uses uv and Task. Common targets:

Goal Task
Sync the environment task install
Format + autofix task format
Lint (format-check + ruff) task lint
Run tests at 100% coverage task test

task --list shows everything. The test suite enforces 100% coverage of the framework and dbt dispatch code (the generated dbt models are excluded — they're schema, not logic). Beyond the synthetic fixtures, real artifacts from a live dbt build live in tests/data/ and round-trip through the public parse() in tests/artifact_parser/dbt/test_roundtrip.py — the only tests that exercise populated nodes end to end.

One non-obvious rule the generator enforces: the generated models are relaxed to pydantic extra="ignore" (not the extra="forbid" dbt's schemas imply), because real artifacts carry fields the published schema omits. A strict model would reject a perfectly good manifest.json. See CLAUDE.md for the why.

CI

GitHub Actions back the same gates:

Workflow What it does
ci.yml Lint + 100%-coverage tests on Python 3.10–3.13, plus a codegen-in-sync job that fails if the committed generated/ drifts from a fresh regen.
schema-watch.yml Weekly (and on demand): probes dbt's published schemas, regenerates, and opens a PR if a new version appeared.
release.yml Build + coverage gate, then PyPI Trusted Publishing on a published Release (or TestPyPI via manual dispatch).

Action versions and Python deps are kept current by Dependabot.

Agentic setup

This repo is wired for Claude Code: a project CLAUDE.md, a parser-author subagent that owns src/, slash commands (/test, /codegen), secret-blocking and post-edit lint hooks, and the context7 MCP for pulling fresh library docs. See CLAUDE.md for the full tour. It will not write your code for you, but it tries hard to keep you from shipping a failing coverage gate.

Support

If this saved you from hand-spelunking a manifest.json, consider fuelling the next release:

buy me a coffee

Metadata

Release files for artifact-parser 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for artifact-parser 1.0.1
File Size Uploaded
artifact_parser-1.0.1.tar.gz 201.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for artifact-parser 1.0.1
File Interpreter ABI Platform
artifact_parser-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 463.4 kB

Release files / artifact_parser-1.0.1.tar.gz

Download URL artifact_parser-1.0.1.tar.gz
Size 201.6 kB
Tags Source
SHA-256 checksum
How to use checksums
7a8f76250fbef1143e886d062de13b892c17ac9e48fcd4c622ad9f7324fdde2f
BLAKE2b-256 checksum
How to use checksums
b289659857b74d0f9803c837c9aac0f37bb2d6b34a0a05a95e9c3920278ff120
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 Aug 10, 2026.

Transparency log

Release files / artifact_parser-1.0.1-py3-none-any.whl

Download URL artifact_parser-1.0.1-py3-none-any.whl
Size 261.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bb269eae6e6782668ae22f2a275aefdd9b98c3e05d77de4aca6a4c6cbaa4b3b8
BLAKE2b-256 checksum
How to use checksums
1e7f14354cf482af3c16b11d48b721b982749c3dbed148e2b68edab7da986a01
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 Aug 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page