Skip to main content

A Python library for converting Atlassian Document Format (ADF) to Markdown

Project description

pyadf

A high-performance Python library for converting Atlassian Document Format (ADF) to Markdown.

Features

  • Rust-powered — parsing and rendering run in native code via PyO3
  • Streaming JSONL API for ETL pipelines processing millions of documents
  • Same Document class API — drop-in upgrade for most users (see changelog for breaking changes)
  • Flexible input — accepts JSON strings, dictionaries, or any ADF node type
  • Comprehensive node support:
    • Text formatting (bold, italic, links)
    • Headings (h1-h6)
    • Lists (bullet, ordered, task lists)
    • Tables with headers and column spans
    • Code blocks with syntax highlighting
    • Blockquotes and panels
    • Status badges, inline cards, block cards, emoji, mentions
  • Type-safe with comprehensive type hints and Python 3.11+ support
  • Eager validation — ADF structure errors surface at construction time, not render time
  • Robust error handling with detailed, context-aware error messages

Installation

pip install pyadf

Prebuilt wheels are available for Linux and macOS (x86_64 and aarch64) and Windows (x86_64).

Usage

Basic Usage

from pyadf import Document

adf_data = {
    "type": "doc",
    "content": [
        {
            "type": "paragraph",
            "content": [
                {"type": "text", "text": "Hello, "},
                {"type": "text", "text": "world!", "marks": [{"type": "strong"}]}
            ]
        }
    ]
}

doc = Document(adf_data)
print(doc.to_markdown())
# Output: Hello, **world!**

Converting from JSON String

from pyadf import Document

adf_json = '{"type": "doc", "content": [...]}'
doc = Document(adf_json)
markdown = doc.to_markdown()

Converting Individual Nodes

from pyadf import Document

node = {
    "type": "heading",
    "attrs": {"level": 2},
    "content": [{"type": "text", "text": "My Heading"}]
}

doc = Document(node)
print(doc.to_markdown())
# Output: ## My Heading

Batch JSONL Processing

For ETL pipelines processing large volumes of ADF documents:

from pyadf import convert_jsonl, MarkdownConfig

# From a JSONL file (one ADF document per line)
for result in convert_jsonl("export.jsonl"):
    print(result)

# From bytes with custom config
config = MarkdownConfig(bullet_marker="*", show_links=True)
for result in convert_jsonl(jsonl_bytes, config=config, batch_size=10_000):
    print(result)

# Error handling modes
from pyadf import ConversionError

for result in convert_jsonl(data, on_error="include"):
    if isinstance(result, ConversionError):
        print(f"Line {result.line_number}: {result.error}")
    else:
        print(result)

convert_jsonl accepts:

  • source: file path (str), raw bytes, or a binary file-like object
  • config: optional MarkdownConfig
  • on_error: "include" (default, yields ConversionError), "skip", or "raise"
  • batch_size: lines per Rust batch (default 10,000)

Error Handling

from pyadf import Document, InvalidJSONError, UnsupportedNodeTypeError

try:
    doc = Document('invalid json')
except InvalidJSONError as e:
    print(f"Invalid JSON: {e}")

try:
    doc = Document({"type": "unsupported_type"})
except UnsupportedNodeTypeError as e:
    print(f"Unsupported node: {e}")

# Known unsupported nodes like "extension" can be skipped or warned on
doc = Document({"type": "extension"})
assert doc.to_markdown() == ""

Known unsupported node handling:

  • Document(...) defaults to on_known_unsupported="warn" and emits UserWarning while skipping known unsupported nodes such as extension
  • Document(..., on_known_unsupported="skip") silently skips known unsupported nodes
  • Document(..., on_known_unsupported="error") raises UnsupportedNodeTypeError

The same on_known_unsupported option is available on convert_jsonl(...).

Customizing Markdown Output

from pyadf import Document, MarkdownConfig

doc = Document(adf_data)

# Default bullet marker is -
doc.to_markdown()  # "- Item 1\n- Item 2"

# Use * for bullet lists
config = MarkdownConfig(bullet_marker="*")
doc.to_markdown(config)  # "* Item 1\n* Item 2"

# Links are shown by default
doc.to_markdown()  # [Link text](http://example.com)

# Hide underlying href while keeping link text marked
config = MarkdownConfig(show_links=False)
doc.to_markdown(config)  # [Link text]
Option Values Default Description
bullet_marker +, -, * - Character used for bullet list items
show_links True, False True Show underlying links in markdown

Known Unsupported Nodes

These node types are recognized but not rendered. By default they are skipped:

  • mediaSingle
  • mediaGroup
  • mediaInline
  • expand
  • rule
  • media
  • embedCard
  • extension

Supported ADF Node Types

ADF Node Type Markdown Output Notes
doc Document root Top-level container
paragraph Plain text with newlines
text Text with optional formatting Supports bold, italic, links
heading # Heading (levels 1-6)
bulletList - Item
orderedList 1. Item
taskList - [ ] Task Checkbox tasks
codeBlock ```language\ncode\n``` Optional language syntax
blockquote > Quote
panel > Panel content Info/warning/error boxes
table Markdown table Supports headers and colspan
status **[STATUS]** Status badges
inlineCard [link] or code block Link previews
emoji Unicode emoji
hardBreak Line break
mention @DisplayName Jira user mentions
blockCard [link] or code block Link previews

Exception Types

  • PyADFError — Base exception for all pyadf errors
  • InvalidJSONError — Raised when JSON parsing fails
  • InvalidInputError — Raised when input type is incorrect
  • InvalidADFError — Raised when ADF structure is invalid
  • MissingFieldError — Raised when required fields are missing
  • InvalidFieldError — Raised when field values are invalid
  • UnsupportedNodeTypeError — Raised when encountering unsupported node types
  • NodeCreationError — Raised when node creation fails

All exceptions include detailed context about the error location in the ADF tree.

Development

Prerequisites

  • Python 3.11+
  • Rust toolchain (stable)
  • maturin (uv tool install maturin)

Setup

git clone https://github.com/YoungseokCh/pyadf.git
cd pyadf
uv sync
uv run maturin develop

Testing

cargo test              # Rust unit tests
uv run pytest tests/ -v # Python tests

Linting

# Rust
cargo fmt --check
cargo clippy -- -D warnings

# Python
ruff check src/ tests/ benchmarks/
ruff format --check src/ tests/ benchmarks/

License

MIT License — see LICENSE file for details.

Changelog

0.4.3

  • Show link targets by default in markdown output
  • Use - as the default bullet marker
  • Treat extension as a known unsupported node instead of failing by default
  • Add on_known_unsupported=error|skip|warn for known unsupported nodes; unknown node types still error

0.4.2

  • Add support for blockCard node type

0.4.1

  • Fix linux x86_64 wheel builds

0.4.0

  • Rust core via PyO3 — 5x faster single-doc, 24x faster batch processing
  • New convert_jsonl() streaming API for batch JSONL processing
  • New ConversionError dataclass for structured batch error handling
  • Build system switched from setuptools to maturin
  • abi3 stable ABI wheels for Linux, macOS (x86_64 + aarch64) and Windows (x86_64)

Breaking changes:

  • Removed set_debug_mode() and _logger module (will be replaced with Rust-native tracing in a future release)
  • nodes and _types modules removed (internal implementation replaced by Rust)

0.3.2

  • Added support for showing href links in markdown output

0.3.1

  • Added mention node support

0.3.0

  • Added emoji node support
  • Added configurable bullet markers via MarkdownConfig

0.1.0

  • Class-based API with Document class
  • Support for common ADF node types
  • Type-safe architecture with comprehensive type hints (Python 3.11+)
  • Flexible input handling (JSON strings, dictionaries, individual nodes)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyadf-0.4.3.tar.gz (47.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyadf-0.4.3-cp311-abi3-win_amd64.whl (255.6 kB view details)

Uploaded CPython 3.11+Windows x86-64

pyadf-0.4.3-cp311-abi3-manylinux_2_34_x86_64.whl (347.8 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.34+ x86-64

pyadf-0.4.3-cp311-abi3-manylinux_2_28_x86_64.whl (348.1 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.28+ x86-64

pyadf-0.4.3-cp311-abi3-manylinux_2_28_aarch64.whl (326.6 kB view details)

Uploaded CPython 3.11+manylinux: glibc 2.28+ ARM64

pyadf-0.4.3-cp311-abi3-macosx_11_0_arm64.whl (306.0 kB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

pyadf-0.4.3-cp311-abi3-macosx_10_12_x86_64.whl (325.8 kB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

File details

Details for the file pyadf-0.4.3.tar.gz.

File metadata

  • Download URL: pyadf-0.4.3.tar.gz
  • Upload date:
  • Size: 47.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyadf-0.4.3.tar.gz
Algorithm Hash digest
SHA256 bd4c66ee8ef80d27e924086d196c96593c6cc08a1383bbee3c491f664c8be17d
MD5 51dff6e3acb54301dcf1b3cd2e90c856
BLAKE2b-256 670510079b887c0ac3396f438fe9fb8dfa1ff0fed2ad9f04489c236274a8acd3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyadf-0.4.3.tar.gz:

Publisher: publish.yml on YoungseokCh/pyadf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyadf-0.4.3-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: pyadf-0.4.3-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 255.6 kB
  • Tags: CPython 3.11+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyadf-0.4.3-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 49240cca6aeae10ff9143ac4528d3f88f50f236b127f7bd69f2ec112c5bf3511
MD5 1365ee4aceef89b32d8c0cf7c025a44b
BLAKE2b-256 d5c88676b8d5ede77fddd81acd3a4656af13d026541cdb6c12a2382e1c8d8d1f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyadf-0.4.3-cp311-abi3-win_amd64.whl:

Publisher: publish.yml on YoungseokCh/pyadf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyadf-0.4.3-cp311-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for pyadf-0.4.3-cp311-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 646aacadb72fcd653d2f0f4e4b24f531a36f0e9517cd3362ce4885f1d8c652b3
MD5 83693ad66c567c09ce12ecc0d6ec85ab
BLAKE2b-256 b3e44a877012ac82ceb131ddbb604b9574c89d53a2de966ea5f7c3632eba132b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyadf-0.4.3-cp311-abi3-manylinux_2_34_x86_64.whl:

Publisher: publish.yml on YoungseokCh/pyadf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyadf-0.4.3-cp311-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for pyadf-0.4.3-cp311-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 86fcbb0eca57c4fa6e5e792e0f5259dbe4f741fe46269dfa7cc5c0e62c154da7
MD5 59e7a63f3f6dd5f3bc0ad617af57364a
BLAKE2b-256 31a6aba3215257a8c0ed7b69b22e0a525360238ae687ec6622474c31d1416763

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyadf-0.4.3-cp311-abi3-manylinux_2_28_x86_64.whl:

Publisher: publish.yml on YoungseokCh/pyadf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyadf-0.4.3-cp311-abi3-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for pyadf-0.4.3-cp311-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 b1b126fdbe31bdd7246ece4f621979207ced8b6a265d5e8c09c322a00a4c21a5
MD5 5fccec6744b222901ce8da27fafd31d4
BLAKE2b-256 47b8c880eefb529386edc8b617a3002f9d3932195c79b0ba394e03e8de200581

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyadf-0.4.3-cp311-abi3-manylinux_2_28_aarch64.whl:

Publisher: publish.yml on YoungseokCh/pyadf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyadf-0.4.3-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: pyadf-0.4.3-cp311-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 306.0 kB
  • Tags: CPython 3.11+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyadf-0.4.3-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 65f86748667e355d9dc88ab6d4ebbc4c26edeb46d06d283081ac31d96db7756a
MD5 ffc4b654323e224d2d40948b047fe6aa
BLAKE2b-256 28c7ff058c46860071690825a2b011afe402a6e1a11ad0ceb18655077217a890

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyadf-0.4.3-cp311-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on YoungseokCh/pyadf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyadf-0.4.3-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pyadf-0.4.3-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 03b8d405ae57739da6d014a478b6c58ea6baa7c4402399b67b34be53cc0367ce
MD5 19e2d6f43e7e7a5e853658c9605ad5e8
BLAKE2b-256 2e7b7bc687461dd157b34bd91192960c6d1c20eba3803e5d497a2e2dc971f2a9

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyadf-0.4.3-cp311-abi3-macosx_10_12_x86_64.whl:

Publisher: publish.yml on YoungseokCh/pyadf

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page