Towncrier

25.8.0 · active · verified Thu Apr 09

Towncrier is a utility for generating useful, summarized news files (often called changelogs) for Python projects. It operates by collecting small, individual "news fragments" that developers create for each change, compiling them into a single, formatted release notes document. This approach helps avoid merge conflicts common with single changelog files and provides a clean separation between developer logs and end-user-facing release notes. Currently at version 25.8.0, Towncrier maintains an active development pace with several releases throughout the year, adding features and supporting new Python versions.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to set up Towncrier in a minimal Python project. It creates a `pyproject.toml` configuration, adds a few news fragments of different types, then uses `towncrier build --draft` to preview the generated news file, and finally `towncrier build --yes` to generate the `NEWS.rst` file and remove the fragments. The example cleans up the created directory afterwards.

import subprocess
import os
from pathlib import Path
import shutil

def run_towncrier_quickstart():
    project_root = Path("./my_project_with_news")
    news_dir = project_root / "newsfragments"
    news_file = project_root / "NEWS.rst"

    # Clean up previous run if any
    if project_root.exists():
        shutil.rmtree(project_root)

    project_root.mkdir()
    news_dir.mkdir()

    # 1. Create pyproject.toml configuration
    pyproject_toml_content = '''
[project]
name = "my-project"
version = "1.0.0"

[tool.towncrier]
directory = "newsfragments"
filename = "NEWS.rst"
issue_format = "#{issue}"
'''
    (project_root / "pyproject.toml").write_text(pyproject_toml_content)

    # 2. Create news fragments
    (news_dir / "123.feature").write_text("Added an awesome new feature.")
    (news_dir / "456.bugfix").write_text("Fixed a critical bug in component X.")
    (news_dir / "789.doc").write_text("Improved documentation for API Y.")

    print(f"\n--- Running 'towncrier build --draft' in {project_root.name} ---")
    subprocess.run(['towncrier', 'build', '--draft'], cwd=project_root, check=True)

    print(f"\n--- Running 'towncrier build' in {project_root.name} ---")
    # 'towncrier build' automatically removes fragments for tracked files by default (since 24.7.0)
    # If you were in a real git repo, you'd then 'git add NEWS.rst' and 'git commit'
    subprocess.run(['towncrier', 'build', '--yes'], cwd=project_root, check=True)

    print("\n--- Generated NEWS.rst content ---")
    print(news_file.read_text())

    print("\n--- News fragments after build ---")
    if list(news_dir.iterdir()):
        print(f"Fragments still exist in {news_dir}. This might happen if they were not git-tracked or --keep was used.")
    else:
        print(f"No fragments found in {news_dir} (expected).")

    # Clean up
    shutil.rmtree(project_root)

run_towncrier_quickstart()

view raw JSON →