Skip to main content

Document model for powerful text parsing and processing in Markdown

Project description

FlexDoc

PyPI version CI License: MIT Python versions

A Markdown parser gives you a block AST but no sentences, sizes, or exact source offsets. An NLP toolkit gives you sentences but no Markdown structure. If you need to know which sentence is in which section, how many words (or LLM tokens) a section holds, or exactly where a link sits in the original text, you end up gluing tools together; the glue breaks on the first edit.

FlexDoc builds a single source-grounded model and exposes its structure as layers over one shared coordinate space: exact [start, end) offsets into one retained, normalized source string. The Markdown layer (blocks, inline elements, typed attributes), the textual layer (paragraphs, sentences, word tokens), and the document layer (heading hierarchy, table of contents) are independent parses of the same text, so cross-cutting questions are simple offset queries. One query primitive (collect()) spans all layers; DocGraph serializes any slice as language-neutral JSON; SpanRef anchors spans by quoted text so references survive edits and reparses.

FlexDoc is a standalone library. chopdiff builds its diff-filtering and windowed-transform layer on top of flexdoc.

Installation

uv add flexdoc
# or: pip install flexdoc

Status

Beta (0.3.x). The core model is established, but the later-stage mechanisms in the stabilization roadmap remain open. Breaking changes before 1.0 bump the minor version, so pin a minor version. See the changelog.

Usage

The primary entry point is FlexDoc:

from flexdoc import FlexDoc, NodeKind, TextUnit

markdown_text = "# Introduction\n\nSee [docs](https://example.com/docs).\n"
doc = FlexDoc.from_text(markdown_text)

# Section hierarchy with rolled-up sizes:
print(doc.section_size_tree(units=(TextUnit.words, TextUnit.sentences)))
# # Introduction  (4 words, 2 sentences)

# Sizes at every grain, including approximate LLM tokens:
print(doc.size_summary())
# 53 bytes (3 lines, 2 paras, 2 sents, 4 words, ~14 tok)

# One query primitive across all layers:
link = doc.collect(kinds={NodeKind.link})[0]
print(link.attrs["url"], link.source_span)
# https://example.com/docs (20, 52)

# Round-trips back to normalized Markdown:
print(doc.reassemble())

Every located source-backed unit carries an exact [start, end) span into normalized source_text; paragraphs and sentences also expose that slice as original_text. Some parser-derived identities can be unlocatable and therefore carry span=None.

FlexDoc delegates Markdown parsing to marko (CommonMark with GFM tables and footnotes) via flowmark, and adds sentence segmentation, the section hierarchy, the flat node table, offset-grounded queries, serialization, and span anchoring on top. Parsing any input never raises: malformed Markdown degrades deterministically and visibly (see the spec and the golden-test corpus that pins this behavior).

The full public surfaces live in the submodules:

  • flexdoc.docs: FlexDoc, Paragraph, Sentence, Section, Block, BlockType, the node table, collect(), DocGraph, SpanRef, and source-linked render/report helpers.
  • flexdoc.docs.wordtoks, flexdoc.docs.search_tokens, flexdoc.docs.token_diffs, and flexdoc.docs.token_mapping: lower-level token, search, diff, and mapping utilities that are not promoted by flexdoc.docs.
  • flexdoc.html: html-in-md, html/plaintext conversion, HTML tag helpers, the content extractor, and timestamp extraction.
  • flexdoc.util: read-time and token-count estimation.

See usage.md for the main workflows, and the worked examples (run with uv run python examples/<name>.py from a checkout):

  • doc_structure.py covers section hierarchy, size rollups, offset lookups, and the block tree.
  • normalized_form.py covers block-type tallies, list-density invariance, and per-section links.
  • backfill_timestamps.py aligns an edited transcript to its timestamped source via token mapping.

Project Docs

For users:

For contributors:


This project was built from simple-modern-uv.

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

flexdoc-0.3.0.tar.gz (418.9 kB view details)

Uploaded Source

Built Distribution

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

flexdoc-0.3.0-py3-none-any.whl (90.1 kB view details)

Uploaded Python 3

File details

Details for the file flexdoc-0.3.0.tar.gz.

File metadata

  • Download URL: flexdoc-0.3.0.tar.gz
  • Upload date:
  • Size: 418.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for flexdoc-0.3.0.tar.gz
Algorithm Hash digest
SHA256 95cdad1551376de53c191c0694a7a8d4a1eb972fc1a2e16b4b4e2452ecc0abaa
MD5 7a25d70f59afd9d2d47e53553c0bca77
BLAKE2b-256 fb9e689c7e6a82ac47fb36035081c89d629c412f07d68a74f2e8d4c8ffc5134c

See more details on using hashes here.

File details

Details for the file flexdoc-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: flexdoc-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 90.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for flexdoc-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a9688cbd74265982b961c85b43b509c89a033ec2a5dfadf6f5ee66841a6e5073
MD5 2f8791da022627942598fc29647ddf66
BLAKE2b-256 a0d09ae7b445da66b2c26a3a138377a0ed0dd973defe38696361081a97459307

See more details on using hashes here.

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