From 8f7206e7975cd74e0d46f6d92a9ecc1c0cdb63aa Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 23 Feb 2026 12:55:47 -0500 Subject: [PATCH 1/4] Migrate from changelog_entry.yaml to towncrier fragments Co-Authored-By: Claude Opus 4.6 --- .github/bump_version.py | 81 ++++++++++++++++++++ Makefile | 8 +- changelog_entry.yaml => changelog.d/.gitkeep | 0 changelog.d/migrate-to-towncrier.changed.md | 1 + pyproject.toml | 36 ++++++++- 5 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 .github/bump_version.py rename changelog_entry.yaml => changelog.d/.gitkeep (100%) create mode 100644 changelog.d/migrate-to-towncrier.changed.md diff --git a/.github/bump_version.py b/.github/bump_version.py new file mode 100644 index 00000000..19aa7907 --- /dev/null +++ b/.github/bump_version.py @@ -0,0 +1,81 @@ +"""Infer semver bump from towncrier fragment types and update version.""" + +import re +import sys +from pathlib import Path + + +def get_current_version(pyproject_path: Path) -> str: + text = pyproject_path.read_text() + match = re.search( + r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE + ) + if not match: + print( + "Could not find version in pyproject.toml", + file=sys.stderr, + ) + sys.exit(1) + return match.group(1) + + +def infer_bump(changelog_dir: Path) -> str: + fragments = [ + f + for f in changelog_dir.iterdir() + if f.is_file() and f.name != ".gitkeep" + ] + if not fragments: + print("No changelog fragments found", file=sys.stderr) + sys.exit(1) + + categories = {f.suffix.lstrip(".") for f in fragments} + for f in fragments: + parts = f.stem.split(".") + if len(parts) >= 2: + categories.add(parts[-1]) + + if "breaking" in categories: + return "major" + if "added" in categories or "removed" in categories: + return "minor" + return "patch" + + +def bump_version(version: str, bump: str) -> str: + major, minor, patch = (int(x) for x in version.split(".")) + if bump == "major": + return f"{major + 1}.0.0" + elif bump == "minor": + return f"{major}.{minor + 1}.0" + else: + return f"{major}.{minor}.{patch + 1}" + + +def update_file(path: Path, old_version: str, new_version: str): + text = path.read_text() + updated = text.replace( + f'version = "{old_version}"', + f'version = "{new_version}"', + ) + if updated != text: + path.write_text(updated) + print(f" Updated {path}") + + +def main(): + root = Path(__file__).resolve().parent.parent + pyproject = root / "pyproject.toml" + changelog_dir = root / "changelog.d" + + current = get_current_version(pyproject) + bump = infer_bump(changelog_dir) + new = bump_version(current, bump) + + print(f"Version: {current} -> {new} ({bump})") + + update_file(pyproject, current, new) + + +if __name__ == "__main__": + main() diff --git a/Makefile b/Makefile index f1e4b163..b54164f4 100644 --- a/Makefile +++ b/Makefile @@ -21,12 +21,8 @@ clean: find . -not -path "./.venv/*" -type f -name "*.h5" -delete changelog: - build-changelog changelog.yaml --output changelog.yaml --update-last-date --start-from 1.0.0 --append-file changelog_entry.yaml - build-changelog changelog.yaml --org PolicyEngine --repo policyengine.py --output CHANGELOG.md --template .github/changelog_template.md - bump-version changelog.yaml pyproject.toml - rm changelog_entry.yaml || true - touch changelog_entry.yaml - + python .github/bump_version.py + towncrier build --yes --version $$(python -c "import re; print(re.search(r'version = \"(.+?)\"', open('pyproject.toml').read()).group(1))") build-package: python -m build diff --git a/changelog_entry.yaml b/changelog.d/.gitkeep similarity index 100% rename from changelog_entry.yaml rename to changelog.d/.gitkeep diff --git a/changelog.d/migrate-to-towncrier.changed.md b/changelog.d/migrate-to-towncrier.changed.md new file mode 100644 index 00000000..865484ad --- /dev/null +++ b/changelog.d/migrate-to-towncrier.changed.md @@ -0,0 +1 @@ +Migrated from changelog_entry.yaml to towncrier fragments to eliminate merge conflicts. diff --git a/pyproject.toml b/pyproject.toml index b01b0fbb..ae6ae582 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,8 @@ dev = [ "ruff>=0.5.0", "policyengine_core>=3.23.6", "policyengine-uk>=2.51.0", - "policyengine-us>=1.213.1", + "policyengine-us>=1.213.1", "towncrier>=24.8.0", + ] [tool.setuptools] @@ -103,3 +104,36 @@ quote-style = "double" indent-style = "space" skip-magic-trailing-comma = false line-ending = "auto" + +[tool.towncrier] +package = "policyengine" +directory = "changelog.d" +filename = "CHANGELOG.md" +title_format = "## [{version}] - {project_date}" +issue_format = "" +underlines = ["", "", ""] + +[[tool.towncrier.type]] +directory = "breaking" +name = "Breaking changes" +showcontent = true + +[[tool.towncrier.type]] +directory = "added" +name = "Added" +showcontent = true + +[[tool.towncrier.type]] +directory = "changed" +name = "Changed" +showcontent = true + +[[tool.towncrier.type]] +directory = "fixed" +name = "Fixed" +showcontent = true + +[[tool.towncrier.type]] +directory = "removed" +name = "Removed" +showcontent = true From 369c2728b526a01ae93ead115fd572fbf883ce0a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 23 Feb 2026 13:03:41 -0500 Subject: [PATCH 2/4] Format bump_version.py with black Co-Authored-By: Claude Opus 4.6 --- .github/bump_version.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/bump_version.py b/.github/bump_version.py index 19aa7907..bb0fd6dd 100644 --- a/.github/bump_version.py +++ b/.github/bump_version.py @@ -7,9 +7,7 @@ def get_current_version(pyproject_path: Path) -> str: text = pyproject_path.read_text() - match = re.search( - r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE - ) + match = re.search(r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE) if not match: print( "Could not find version in pyproject.toml", From cbe708e8f108e000b63e59ce27eb9ee6aae203c3 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 23 Feb 2026 13:12:05 -0500 Subject: [PATCH 3/4] Fix ruff UP042: use StrEnum instead of (str, Enum) Co-Authored-By: Claude Opus 4.6 --- src/policyengine/outputs/aggregate.py | 4 ++-- src/policyengine/outputs/change_aggregate.py | 4 ++-- src/policyengine/outputs/poverty.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/policyengine/outputs/aggregate.py b/src/policyengine/outputs/aggregate.py index 2d41259c..cf4dbca4 100644 --- a/src/policyengine/outputs/aggregate.py +++ b/src/policyengine/outputs/aggregate.py @@ -1,10 +1,10 @@ -from enum import Enum +from enum import StrEnum from typing import Any from policyengine.core import Output, Simulation -class AggregateType(str, Enum): +class AggregateType(StrEnum): SUM = "sum" MEAN = "mean" COUNT = "count" diff --git a/src/policyengine/outputs/change_aggregate.py b/src/policyengine/outputs/change_aggregate.py index b5bfe2df..29fe5069 100644 --- a/src/policyengine/outputs/change_aggregate.py +++ b/src/policyengine/outputs/change_aggregate.py @@ -1,10 +1,10 @@ -from enum import Enum +from enum import StrEnum from typing import Any from policyengine.core import Output, Simulation -class ChangeAggregateType(str, Enum): +class ChangeAggregateType(StrEnum): COUNT = "count" SUM = "sum" MEAN = "mean" diff --git a/src/policyengine/outputs/poverty.py b/src/policyengine/outputs/poverty.py index 9b5074f4..4955b814 100644 --- a/src/policyengine/outputs/poverty.py +++ b/src/policyengine/outputs/poverty.py @@ -1,6 +1,6 @@ """Poverty analysis output types.""" -from enum import Enum +from enum import StrEnum from typing import Any import pandas as pd @@ -9,7 +9,7 @@ from policyengine.core import Output, OutputCollection, Simulation -class UKPovertyType(str, Enum): +class UKPovertyType(StrEnum): """UK poverty measure types.""" ABSOLUTE_BHC = "absolute_bhc" @@ -18,7 +18,7 @@ class UKPovertyType(str, Enum): RELATIVE_AHC = "relative_ahc" -class USPovertyType(str, Enum): +class USPovertyType(StrEnum): """US poverty measure types.""" SPM = "spm" From a06997a5bfdaf024985e4d0e5ca23ebf11d88094 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 24 Feb 2026 05:42:28 -0500 Subject: [PATCH 4/4] Delete old changelog files --- changelog.yaml | 242 ------------------------------------------------- 1 file changed, 242 deletions(-) delete mode 100644 changelog.yaml diff --git a/changelog.yaml b/changelog.yaml deleted file mode 100644 index 3f7d8354..00000000 --- a/changelog.yaml +++ /dev/null @@ -1,242 +0,0 @@ -- changes: - added: - - Initial version of package. - date: 2024-11-30 00:00:00 - version: 0.1.0 -- bump: patch - changes: - fixed: - - Dependency for `pkg_resources`. - date: 2025-02-21 14:02:22 -- bump: patch - changes: - fixed: - - Gracefully handle data download 429s. - date: 2025-04-26 16:21:55 -- bump: patch - changes: - fixed: - - Gracefully handle HuggingFace 429s. - date: 2025-04-26 16:25:19 -- bump: minor - changes: - added: - - Google Cloud Storage data downloads. - date: 2025-04-29 13:08:11 -- bump: patch - changes: - added: - - Default dataset handling (extra backups added). - fixed: - - Bug in state tax revenue calculation. - date: 2025-04-30 11:33:36 -- bump: minor - changes: - changed: - - Economy simulation comparisons now include country package version - date: 2025-05-13 23:23:01 -- bump: patch - changes: - changed: - - Refactored ParametricReform schema into clearer subschemas. - - Added conversion of Infinity and -Infinity to np.inf and -np.inf. - date: 2025-05-15 15:52:24 -- bump: patch - changes: - added: - - new class CachingGoogleStorageClient for locally caching gs files to disk. - date: 2025-05-15 21:56:02 -- bump: patch - changes: - added: - - Changelog entry check. - date: 2025-05-16 12:23:45 -- bump: patch - changes: - fixed: - - Fixed `format_fig` to work with Python 3.11. - date: 2025-05-16 12:55:50 -- bump: patch - changes: - fixed: - - downloads from google storage should now be properly cached. - date: 2025-05-16 14:00:30 -- bump: patch - changes: - fixed: - - Removed max and min year bounds for Simulations. - date: 2025-05-16 16:18:15 -- bump: patch - changes: - fixed: - - revert cached downloads from google storage - date: 2025-05-19 16:53:05 -- bump: patch - changes: - fixed: - - google storage caching is now fully sync, not async and reenabled. - date: 2025-05-19 22:42:39 -- bump: patch - changes: - fixed: - - Added cliff impacts. - date: 2025-05-20 08:28:29 -- bump: patch - changes: - fixed: - - Always look for new data file versions even if we have a local copy of one. - date: 2025-05-23 00:09:23 -- bump: minor - changes: - added: - - Error handling for data and package version mismatches. - date: 2025-05-26 21:26:48 -- bump: patch - changes: - fixed: - - Model and data versions are always available. - date: 2025-05-27 11:10:28 -- bump: patch - changes: - added: - - Tests for Simulation._set_data() - changed: - - Disambiguated filepath management in Simulation._set_data() - - Refactored Simulation._set_data() to divide functionality into smaller methods - - Prevented passage of non-Path URIs to Dataset.from_file() at end of Simulation._set_data() - execution - date: 2025-05-30 21:12:45 -- bump: patch - changes: - fixed: - - Removed bad type check. - date: 2025-06-02 07:53:55 -- bump: patch - changes: - fixed: - - ECPS handling issue. - date: 2025-06-04 07:35:40 -- bump: patch - changes: - fixed: - - Added more log messages. - date: 2025-06-05 05:08:16 -- bump: minor - changes: - added: - - Unit test for GeneralEconomyTask.calculate_cliffs() and fixture for the test - - Test for calculate_single_economy with cliff impacts - date: 2025-06-26 20:04:46 -- bump: minor - changes: - fixed: - - Updated the UK data to the latest survey year. - date: 2025-07-17 10:27:55 -- bump: patch - changes: - fixed: - - Fixed UK dataset loading issue. - date: 2025-08-14 08:11:19 -- bump: major - changes: - added: - - Complete rewrite. - date: 2025-09-21 22:26:17 -- bump: major - changes: - added: - - Consolidated models and database integration. - date: 2025-09-21 22:29:03 -- bump: major - changes: - fixed: - - Major version bump to fix pypi issues. - date: 2025-09-23 08:43:22 -- bump: minor - changes: - - Just basemodels, no sqlmodels. - - Clean, working analysis at both household and macro level for uk and us. - date: 2025-11-17 10:25:50 -- bump: patch - changes: - added: - - Policy and Dynamic classes now support addition operator (__add__) to combine - policies and dynamics. - - Parameter values are appended when combining policies/dynamics. - - Simulation modifiers are chained in sequence when combining policies/dynamics. - date: 2025-11-17 11:45:43 -- bump: patch - changes: - fixed: - - Standardised saving and loading of simulations. - date: 2025-11-18 11:09:18 -- bump: patch - changes: - fixed: - - Entity variables moved out to an editable constant. - date: 2025-11-18 13:46:23 -- bump: patch - changes: - fixed: - - Minor fixes - date: 2025-11-20 14:06:49 -- bump: patch - changes: - fixed: - - Minor fixes - date: 2025-11-21 12:59:36 -- bump: patch - changes: - fixed: - - Parameter values now accessible from models. - date: 2025-11-24 16:23:57 -- bump: patch - changes: - fixed: - - Build error - date: 2025-11-24 16:34:53 -- bump: patch - changes: - fixed: - - Dataset speedup with better handling of string cols. - date: 2025-12-02 00:20:11 -- bump: patch - changes: - fixed: - - Added caching of saved simulations - date: 2025-12-02 12:48:37 -- bump: patch - changes: - fixed: - - Caching failure. - date: 2025-12-02 13:02:37 -- bump: patch - changes: - fixed: - - Caching didn't save time! - date: 2025-12-02 14:03:39 -- bump: patch - changes: - fixed: - - Bug where all datasets would be the last year. - date: 2025-12-10 14:43:52 -- bump: patch - changes: - fixed: - - Naming improvements. - date: 2025-12-10 19:29:28 -- bump: patch - changes: - fixed: - - Improvements to loading taxbenefitsystems. - date: 2025-12-10 21:59:24 -- bump: patch - changes: - added: - - Household impacts - date: 2025-12-14 23:51:27 -- bump: patch - changes: - changed: - - Bumped policyengine-core minimum version to 3.23.5 for pandas 3.0 compatibility - date: 2026-01-25 14:20:29