Skip to main content

Rust-backed Python chunking library

Project description

py-chunks

Python License

Fast, framework-agnostic document chunking library backed by Rust. Extract meaningful content segments from DOCX, PDF, PPTX, TXT, MD, and HTML files—optimized for production use.

Features

  • 6 Document Formats: DOCX, PDF, PPTX, TXT, Markdown, HTML
  • Zero Dependencies: Pure Python stdlib; all parsing is Rust-compiled
  • Framework Agnostic: Local files, bytes, file-like objects, FastAPI uploads, S3 presigned URLs, or raw streams
  • Consistent Output Schema: Structured chunks with content type, section headings, and format-specific metadata
  • High Performance: Rust parsing engine with millisecond latency per document
  • Production Ready: Full type hints, comprehensive error handling, extensive test coverage, Pylint 10.00/10

Installation

pip install py-chunks

Requirements: Python 3.9+

Quick Start

from py_chunks import get_chunks

# Auto-detects source type and format
chunks = get_chunks("document.pdf")

for chunk in chunks:
    print(chunk["content"])
    print(chunk["content_type"])  # "heading", "plain_paragraph", etc.

Chunking Modes (DOCX)

By default, DOCX files use structural chunking. Pass mode to switch strategies:

chunks = get_chunks("file.docx", mode="default")      # default
chunks = get_chunks("file.docx", mode="structural")   # equivalent to default
chunks = get_chunks("file.docx", mode="section")
chunks = get_chunks("file.docx", mode="semantic")
chunks = get_chunks("file.docx", mode="sliding_window")
chunks = get_chunks("file.docx", mode="sentence")
chunks = get_chunks("file.docx", mode="page_aware")

structural (default)

Splits by document structure — headings, paragraphs, tables, lists. Each element becomes its own chunk with a typed content_type. content_type values: heading, plain_paragraph, table, bullet_list, code_block

section

Groups everything under a heading into one chunk including all sub-paragraphs, lists, and tables until the next heading of equal or higher level. Sections exceeding 2000 chars are split automatically.

chunks = get_chunks("file.docx", mode="section")
# chunk["metadata"] contains: section_heading, section_level, heading_path

semantic

Groups paragraphs by topic continuity using pure heuristics — no ML models or external dependencies. Signals used (in priority order):

  • Reference continuity: paragraph starts with This/It/They/These
  • Transition keywords: However/Therefore/In contrast -> new chunk
  • Keyword overlap: shared significant words -> merge
  • Short paragraph merging: paragraphs under 80 chars merged with next
  • Max chunk size: 1500 chars hard limit
chunks = get_chunks("file.docx", mode="semantic")
# chunk["metadata"] contains: paragraph_count, merge_reason

sliding_window

Fixed-size overlapping windows of paragraphs. Useful for RAG with overlap context.

chunks = get_chunks("file.docx", mode="sliding_window",
                    window_size=3, overlap=1)
# defaults: window_size=3, overlap=1
# chunk["metadata"] contains: window_size, overlap, window_index,
#                              paragraph_indices

sentence

Splits content into sentence-level chunks grouped by sentences_per_chunk. Sentence boundaries detected without NLP libraries using punctuation rules. Handles common abbreviations (Mr. Dr. vs. etc.) without false splits.

chunks = get_chunks("file.docx", mode="sentence",
                    sentences_per_chunk=3)
# defaults: sentences_per_chunk=3
# chunk["metadata"] contains: sentences_per_chunk,
#                              actual_sentence_count, chunk_index,
#                              source_paragraph_index

page_aware

Approximates page boundaries using 3 signals in priority order:

  1. Explicit page breaks (w:pageBreak) — most reliable
  2. Section breaks (w:sectPr) — reliable
  3. Paragraph count approximation — fallback (~15 per page)
chunks = get_chunks("file.docx", mode="page_aware",
                    paragraphs_per_page=15)
# defaults: paragraphs_per_page=15
# chunk["metadata"] contains: page_number, page_break_type,
#                              paragraph_count
# page_break_type values: "explicit", "section", "estimated"

Note: Mode-based chunking applies to DOCX and PDF. For both formats, mode="default" is the API default. DOCX treats "default" and "structural" as equivalent behavior. PPTX, HTML, MD, and TXT currently use format-specific structural/default chunking without additional modes.

Supported Sources

The library accepts documents from multiple sources with automatic type detection:

Source Method Example
Local File Path get_chunks() or get_chunks_from_path() get_chunks("report.pdf")
Raw Bytes get_chunks_from_bytes() get_chunks_from_bytes(data, filename="report.pdf")
File-like Object get_chunks_from_fileobj() get_chunks_from_fileobj(BytesIO(...), filename="doc.md")
FastAPI Upload get_chunks_from_upload() get_chunks_from_upload(file)
S3 Presigned URL get_chunks_from_s3_presigned_url() get_chunks_from_s3_presigned_url(url)

Supported Formats

Format Extensions
PDF .pdf
DOCX .docx
PPTX .pptx
Markdown .md
HTML .html, .htm
Plain Text .txt

Usage Examples

From local file:

from py_chunks import get_chunks

chunks = get_chunks("report.pdf")

From bytes (API upload):

from py_chunks import get_chunks_from_bytes

file_bytes = request.files['document'].read()
chunks = get_chunks_from_bytes(file_bytes, filename="report.pdf")

From FastAPI upload:

from fastapi import FastAPI, File, UploadFile
from py_chunks import get_chunks_from_upload

app = FastAPI()

@app.post("/upload/")
async def chunk_document(file: UploadFile = File(...)):
    chunks = get_chunks_from_upload(file)
    return {"chunks": chunks}

From file-like object:

from py_chunks import get_chunks_from_fileobj
from io import BytesIO

bio = BytesIO(file_data)
chunks = get_chunks_from_fileobj(bio, filename="document.md")

From S3 presigned URL:

from py_chunks import get_chunks_from_s3_presigned_url

url = "https://bucket.s3.amazonaws.com/file.docx?AWSAccessKeyId=..."
chunks = get_chunks_from_s3_presigned_url(url)

API Reference

Universal Entry Point

get_chunks(source, *, filename=None, mode="default", window_size=3, overlap=1, sentences_per_chunk=3, paragraphs_per_page=15) -> list[dict]

Accepts any source type and automatically detects format and dispatch target.

stream_chunks(source, *, filename=None, mode="default", window_size=3, overlap=1, sentences_per_chunk=3, paragraphs_per_page=15) -> Iterator[dict]

Streaming equivalent of get_chunks() with the same default mode semantics.

Parameters:

  • source: str, PathLike, bytes, file-like object, upload object, or URL
  • filename: Optional filename to disambiguate format (required for bytes/file objects without .filename attribute)

Returns: list[dict] – Each chunk is a dictionary with:

  • content: str – Extracted text segment
  • content_type: str – Content classification
  • metadata: dict – Section heading, page number, source type, format-specific fields

Raises: FileNotFoundError, ValueError, TypeError with descriptive messages

Format-Specific Entry Points

  • get_chunks_from_path(file_path: str) -> list[dict] – Local filesystem
  • get_chunks_from_bytes(data: bytes, filename: str) -> list[dict] – Raw bytes (temp file auto-cleanup)
  • get_chunks_from_fileobj(file_obj, filename: str | None = None) -> list[dict] – File-like objects (open(), BytesIO, etc.)
  • get_chunks_from_upload(upload_file) -> list[dict] – FastAPI/Starlette uploads (handles async)
  • get_chunks_from_s3_presigned_url(url: str, filename: str | None = None, timeout: int = 60) -> list[dict] – HTTP/HTTPS URLs

Advanced: Format-Specific Chunkers

Direct access to format parsers (returns timing information):

from py_chunks.chunkers import chunk_pdf, chunk_docx, chunk_html, chunk_md, chunk_pptx, chunk_txt

chunks, timing = chunk_pdf("file.pdf")
print(f"Rust: {timing['rust_ms']}ms, Python: {timing['python_ms']}ms")

Each format-specific chunker returns a tuple: (list[dict], dict) where the second element contains rust_ms and python_ms keys.

Output Schema

Chunk Structure

{
    "content": "The extracted text segment.",
    "content_type": "plain_paragraph",  # See content types below
    "metadata": {
        "section_heading": "Section Title" | None,
        "footnotes_captions": [...],
        "page_number": 1 | None,
        "document_metadata": {
            "source_type": "pdf",
            # format-specific fields
        }
    }
}

Content Types

  • heading – Section heading (H1-H6, bold text, etc.)
  • plain_paragraph – Prose paragraph
  • bullet_list – Unordered list
  • table – Tabular data
  • code_block – Code or preformatted text
  • long_single_paragraph – Paragraph > 900 characters
  • short_disconnected_paragraph – Paragraph < 90 characters
  • section – Heading-scoped grouped content
  • semantic – Heuristic topic-continuity grouped content
  • sliding_window – Fixed-size overlapping paragraph windows
  • sentence – Sentence-count grouped content
  • page_aware – Page boundary grouped content

Architecture

System Design

┌──────────────────────────────────┐
│     Python Public API            │
│   (py_chunks/__init__.py)        │
│  ├─ get_chunks()                 │
│  ├─ get_chunks_from_path()       │
│  ├─ get_chunks_from_bytes()      │
│  ├─ get_chunks_from_fileobj()    │
│  ├─ get_chunks_from_upload()     │
│  └─ get_chunks_from_s3_presigned_url() │
└────────────┬─────────────────────┘
             │
             ↓
┌──────────────────────────────────┐
│    Format Dispatcher             │
│  (py_chunks/chunkers/*.py)       │
│  ├─ chunk_pdf()                  │
│  ├─ chunk_docx()                 │
│  ├─ chunk_pptx()                 │
│  ├─ chunk_html()                 │
│  ├─ chunk_md()                   │
│  └─ chunk_txt()                  │
└────────────┬─────────────────────┘
             │
             ↓
┌──────────────────────────────────┐
│     Rust Extension Module        │
│    (src/extensions/*.rs)         │
│  • Format-specific parsing       │
│  • Returns PyDict chunks         │
│  • Timing instrumentation        │
└──────────────────────────────────┘

Design Principles

  • Single Responsibility: Each format parser handles only its format
  • Framework Agnostic: Python layer abstracts source complexity; Rust layer is format-specific
  • Temp File Strategy: Bytes → temp file → Rust → auto-cleanup provides safety without complex FFI
  • Zero Dependencies: Pure Python stdlib; all parsing is Rust-compiled

Development & Testing

Running Tests

cd py_chunks
python -m pytest -v

Expected: 15 integration tests passing

Code Quality

python -m pylint py_chunks tests/test_source_apis.py

Expected: 10.00/10 score

Local Development Build

pip install -e .

Rebuilds the Rust extension with local changes.

Error Handling

All functions raise descriptive exceptions:

from py_chunks import get_chunks

try:
    chunks = get_chunks("missing.pdf")
except FileNotFoundError as e:
    print(e)  # File not found: missing.pdf

try:
    chunks = get_chunks("image.png")
except ValueError as e:
    print(e)  # Unsupported file type '.png'. Supported: .docx, .htm, .html, .md, .pdf, .pptx, .txt

Framework Integration

Flask

from flask import Flask, request
from py_chunks import get_chunks_from_bytes

app = Flask(__name__)

@app.post("/chunk")
def chunk_document():
    file = request.files['document']
    chunks = get_chunks_from_bytes(file.read(), file.filename)
    return {"chunks": chunks}

FastAPI

from fastapi import FastAPI, File, UploadFile
from py_chunks import get_chunks_from_upload

app = FastAPI()

@app.post("/chunk/")
async def chunk_document(file: UploadFile = File(...)):
    chunks = get_chunks_from_upload(file)
    return {"chunks": chunks}

Django

from django.http import JsonResponse
from py_chunks import get_chunks_from_upload

def chunk_view(request):
    if request.FILES:
        file = request.FILES['document']
        chunks = get_chunks_from_upload(file)
        return JsonResponse({"chunks": chunks})
    return JsonResponse({"error": "No file"}, status=400)

Celery Background Job

import celery
from py_chunks import get_chunks

@celery.task
def process_document(file_path: str):
    chunks = get_chunks(file_path)
    # Persist to database
    return len(chunks)

License

MIT


Built with Rust (performance) + Python (simplicity)

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

py_chunks-0.1.5-cp313-cp313-win_amd64.whl (1.2 MB view details)

Uploaded CPython 3.13Windows x86-64

py_chunks-0.1.5-cp313-cp313-manylinux_2_34_x86_64.whl (1.8 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.34+ x86-64

py_chunks-0.1.5-cp313-cp313-macosx_11_0_arm64.whl (1.4 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

File details

Details for the file py_chunks-0.1.5-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: py_chunks-0.1.5-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for py_chunks-0.1.5-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 aee7a70a8b586aea7eeff45d9831049273a65e560b5e1e301a41fb81bbd69a2e
MD5 8ad11758de453b2b9c66e2e5d8a720fe
BLAKE2b-256 cdfc2ddf6c21c584679bdd55cbadd9825162dc71393222a022de1058b504392c

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_chunks-0.1.5-cp313-cp313-win_amd64.whl:

Publisher: release.yml on RanjanKudesia/py-chunks

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

File details

Details for the file py_chunks-0.1.5-cp313-cp313-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for py_chunks-0.1.5-cp313-cp313-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 0603956ba5b0a97490fd5549ed3ffd505aba38cd7ff8136b3e8b4f1ee9cd2a37
MD5 a91deabbabdccf06fc76d6da621b1860
BLAKE2b-256 3d9cafae06f5d62a7f3b2b09255802d72687946fb99c2ab95f5de4c64f5e5ebd

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_chunks-0.1.5-cp313-cp313-manylinux_2_34_x86_64.whl:

Publisher: release.yml on RanjanKudesia/py-chunks

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

File details

Details for the file py_chunks-0.1.5-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for py_chunks-0.1.5-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 351d3b45552b143bab482be05086a41bbe22636a4861754ff9aa0b0d91625f4d
MD5 8c607967ad5241ed746252ef2272888a
BLAKE2b-256 2f3ad06688c0a1b8743a44de454f962832cfdef7d5494294c85bc04b152bbb8b

See more details on using hashes here.

Provenance

The following attestation bundles were made for py_chunks-0.1.5-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: release.yml on RanjanKudesia/py-chunks

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