{"id":2316,"library":"towncrier","title":"Towncrier","description":"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.","status":"active","version":"25.8.0","language":"en","source_language":"en","source_url":"https://github.com/twisted/towncrier","tags":["changelog","news","release notes","automation","development workflow"],"install":[{"cmd":"pip install towncrier","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Command-line interface toolkit","package":"click"},{"reason":"Templating engine for news file generation","package":"Jinja2"},{"reason":"Parsing pyproject.toml configuration (implicitly for Python <3.11)","package":"toml","optional":true},{"reason":"Used by 'towncrier build' for fragment removal and 'towncrier check'","package":"git","optional":true},{"reason":"Optional SCM support (added in 25.8.0)","package":"mercurial","optional":true}],"imports":[{"note":"Towncrier is primarily a command-line tool. While it has an internal Python API, direct programmatic usage of its core logic is not the intended or commonly supported use case. Most interactions are through the `towncrier` executable via `subprocess` calls.","symbol":"towncrier","correct":"import subprocess\nsubprocess.run(['towncrier', 'build'])"}],"quickstart":{"code":"import subprocess\nimport os\nfrom pathlib import Path\nimport shutil\n\ndef run_towncrier_quickstart():\n    project_root = Path(\"./my_project_with_news\")\n    news_dir = project_root / \"newsfragments\"\n    news_file = project_root / \"NEWS.rst\"\n\n    # Clean up previous run if any\n    if project_root.exists():\n        shutil.rmtree(project_root)\n\n    project_root.mkdir()\n    news_dir.mkdir()\n\n    # 1. Create pyproject.toml configuration\n    pyproject_toml_content = '''\n[project]\nname = \"my-project\"\nversion = \"1.0.0\"\n\n[tool.towncrier]\ndirectory = \"newsfragments\"\nfilename = \"NEWS.rst\"\nissue_format = \"#{issue}\"\n'''\n    (project_root / \"pyproject.toml\").write_text(pyproject_toml_content)\n\n    # 2. Create news fragments\n    (news_dir / \"123.feature\").write_text(\"Added an awesome new feature.\")\n    (news_dir / \"456.bugfix\").write_text(\"Fixed a critical bug in component X.\")\n    (news_dir / \"789.doc\").write_text(\"Improved documentation for API Y.\")\n\n    print(f\"\\n--- Running 'towncrier build --draft' in {project_root.name} ---\")\n    subprocess.run(['towncrier', 'build', '--draft'], cwd=project_root, check=True)\n\n    print(f\"\\n--- Running 'towncrier build' in {project_root.name} ---\")\n    # 'towncrier build' automatically removes fragments for tracked files by default (since 24.7.0)\n    # If you were in a real git repo, you'd then 'git add NEWS.rst' and 'git commit'\n    subprocess.run(['towncrier', 'build', '--yes'], cwd=project_root, check=True)\n\n    print(\"\\n--- Generated NEWS.rst content ---\")\n    print(news_file.read_text())\n\n    print(\"\\n--- News fragments after build ---\")\n    if list(news_dir.iterdir()):\n        print(f\"Fragments still exist in {news_dir}. This might happen if they were not git-tracked or --keep was used.\")\n    else:\n        print(f\"No fragments found in {news_dir} (expected).\")\n\n    # Clean up\n    shutil.rmtree(project_root)\n\nrun_towncrier_quickstart()","lang":"python","description":"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."},"warnings":[{"fix":"Upgrade to Python 3.9 or newer. If unable to upgrade Python, pin Towncrier to a version older than 25.8.0 (e.g., `towncrier<25.8.0`).","message":"Python 3.8 support has been removed in Towncrier 25.8.0. Users on Python 3.8 or older will need to upgrade their Python version or use an earlier Towncrier release.","severity":"breaking","affected_versions":"25.8.0+"},{"fix":"Standardize on one configuration file (`pyproject.toml` is generally recommended for modern Python projects) or be aware of the precedence rules when mixing them.","message":"Towncrier uses a specific configuration file precedence: `towncrier.toml` takes precedence over `pyproject.toml`. If both exist, settings in `towncrier.toml` will override those in `pyproject.toml`.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Always use `towncrier build --keep` if you want to retain the fragment files after generation, or ensure your workflow accounts for their automatic removal. Note that fragments not tracked by Git may not be removed automatically.","message":"By default, `towncrier build` will remove news fragments after successfully generating the news file. If you wish to keep the fragments (e.g., for review before manual deletion), you must use the `--keep` flag.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Define your custom fragment types using the array of tables syntax (e.g., `[[tool.towncrier.type]]` for each type) to explicitly define their order in the generated news file.","message":"When defining custom news fragment types in your configuration, using a TOML mapping (e.g., `[tool.towncrier.fragment.mytype]`) will result in fragment types being sorted alphabetically in the output. To control the order, use a TOML array of tables (`[[tool.towncrier.type]]`).","severity":"gotcha","affected_versions":"Versions using TOML configuration, particularly since changes around 22.8.0"},{"fix":"Migrate your configuration from `towncrier.ini` to `pyproject.toml` under the `[tool.towncrier]` table or a dedicated `towncrier.toml` file.","message":"Support for the `towncrier.ini` configuration file was removed in favor of `pyproject.toml` (and `towncrier.toml`).","severity":"deprecated","affected_versions":"17.8.0+"}],"env_vars":null,"last_verified":"2026-04-09T00:00:00.000Z","next_check":"2026-07-08T00:00:00.000Z"}